MAATRIX / Блог / Saleor на Ubuntu 24.04: пошаговая установка

Saleor на Ubuntu 24.04: пошаговая установка

MAATRIX

Saleor — не «ещё один WooCommerce», а GraphQL-first e-commerce платформа с раздельным бэкендом (Python/Django + Celery) и фронтендом (React Dashboard, storefront на Next.js), рассчитанная на нагруженные проекты с кастомной логикой. Ставится она не одним apt install, а связкой из нескольких сервисов, и именно на этом моменте новички теряют время: пробуют завести всё вручную, путаются в переменных окружения и очередях Celery. Ниже — рабочий путь через Docker Compose с нуля до открытого GraphQL Playground и панели администратора.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Что понадобится и почему без Docker тут не обойтись

Saleor Core сам по себе — Django-приложение, но продакшен-инсталляция требует минимум пяти компонентов: PostgreSQL (основная БД), Redis (брокер для Celery и кеш), сам API-контейнер, воркер Celery для фоновых задач (отправка писем, обработка вебхуков, пересчёт цен) и Dashboard — отдельное SPA-приложение, которое общается с API только по GraphQL. Собирать это вручную из исходников имеет смысл, если вы разработчик Saleor и правите ядро; для запуска магазина куда практичнее использовать официальные Docker-образы и Docker Compose — так вы получаете воспроизводимую конфигурацию и не тащите в систему весь Python/Node toolchain.

Понадобится:

  • VPS с Ubuntu 24.04, минимум 2 vCPU / 4 ГБ RAM для теста; для продакшена с реальным трафиком ориентируйтесь на 4 vCPU / 8 ГБ и выше — точную цифру даст только нагрузочное тестирование под ваш каталог и трафик;
  • домен (лучше сразу два поддомена: api.example.com под GraphQL-эндпоинт и admin.example.com под Dashboard), A-записи указывают на IP сервера;
  • root-доступ или пользователь с sudo;
  • открытые порты 80/443 наружу.

Если сервер вы ещё выбираете — под Saleor подходит обычный VPS с NVMe и запасом по памяти под Celery-воркеры и Postgres.

Устанавливаем Docker и Docker Compose

Подключаемся по SSH и обновляем систему:

apt update && apt upgrade -y
apt install -y ca-certificates curl gnupg git

Ставим Docker Engine из официального репозитория Docker (пакет docker.io из Ubuntu-репов часто отстаёт по версии Compose-плагина):

install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Проверяем:

docker --version
docker compose version

Если хотите развернуть Docker с нуля более подробно и разобраться с правами непривилегированного пользователя, есть отдельный разбор — пошаговая установка Docker Compose для продакшена.

Не забудьте про фаервол — открываем только необходимое:

ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Разворачиваем Saleor через Docker Compose

Создаём рабочую директорию и структуру проекта:

mkdir -p /opt/saleor && cd /opt/saleor

Собираем docker-compose.yml с ключевыми сервисами Saleor: базой, Redis, API, воркером Celery и Dashboard. Версии образов проверьте перед запуском в официальном реестре ghcr.io/saleor — платформа развивается быстро, и на момент вашего чтения актуальным может быть более свежий тег, чем указанный ниже для примера.

services:
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: saleor
      POSTGRES_PASSWORD: change_me_strong_password
      POSTGRES_DB: saleor
    volumes:
      - saleor-db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U saleor"]
      interval: 5s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    volumes:
      - saleor-redis:/data

  api:
    image: ghcr.io/saleor/saleor:3.20
    restart: unless-stopped
    command: gunicorn saleor.wsgi:application --bind 0.0.0.0:8000 --workers 3
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    ports:
      - "127.0.0.1:8000:8000"
    volumes:
      - saleor-media:/app/media
      - saleor-static:/app/static
    env_file: .env

  worker:
    image: ghcr.io/saleor/saleor:3.20
    restart: unless-stopped
    command: celery -A saleor worker --loglevel=info -B
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    volumes:
      - saleor-media:/app/media
    env_file: .env

  dashboard:
    image: ghcr.io/saleor/saleor-dashboard:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:9000:80"

volumes:
  saleor-db:
  saleor-redis:
  saleor-media:
  saleor-static:

Файл .env рядом с compose-файлом — сюда выносим секреты и адреса:

cat > /opt/saleor/.env << 'EOF'
SECRET_KEY=сгенерируйте_длинную_случайную_строку
DEBUG=False
ALLOWED_HOSTS=api.example.com,localhost
DATABASE_URL=psql://saleor:change_me_strong_password@db:5432/saleor
CELERY_BROKER_URL=redis://redis:6379/1
DEFAULT_FROM_EMAIL=shop@example.com
DASHBOARD_URL=https://admin.example.com/
ALLOWED_CLIENT_HOSTS=admin.example.com
EOF

SECRET_KEY сгенерируйте, например, командой openssl rand -base64 48 — не оставляйте значение из примера. Пароль базы в docker-compose.yml и DATABASE_URL должны совпадать; в реальном проекте вынесите пароль тоже в .env и подставьте переменной.

Поднимаем стек и применяем миграции:

cd /opt/saleor
docker compose up -d db redis
sleep 10
docker compose up -d api worker dashboard
docker compose exec api python3 manage.py migrate
docker compose exec api python3 manage.py collectstatic --noinput

Проверяем, что все контейнеры живы:

docker compose ps
docker compose logs -f api

Если api падает в рестарт-луп — почти всегда дело в неверном DATABASE_URL или в том, что db ещё не успел подняться при первом старте; команда docker compose restart api после готовности базы обычно решает проблему.

Nginx как reverse-proxy и SSL для API и Dashboard

Контейнеры слушают только 127.0.0.1, наружу их отдаёт Nginx. Ставим:

apt install -y nginx

Конфиг для API (/etc/nginx/sites-available/saleor-api):

server {
    listen 80;
    server_name api.example.com;

    client_max_body_size 20m;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

И для Dashboard (/etc/nginx/sites-available/saleor-dashboard):

server {
    listen 80;
    server_name admin.example.com;

    location / {
        proxy_pass http://127.0.0.1:9000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Активируем и перезагружаем:

ln -s /etc/nginx/sites-available/saleor-api /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/saleor-dashboard /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

Общие принципы настройки reverse-proxy на Nginx (буферы, тайм-ауты, вебсокеты) подробно разобраны в статье Nginx как reverse-proxy: пошаговая установка — Saleor это касается напрямую, GraphQL-запросы на загрузку каталога бывают увесистыми.

SSL получаем через Certbot:

apt install -y certbot python3-certbot-nginx
certbot --nginx -d api.example.com -d admin.example.com

Certbot сам пропишет listen 443 ssl и настроит редирект с 80 на 443. Если хотите разобраться в альтернативах и нюансах продления сертификатов — есть отдельный гайд про установку Let's Encrypt SSL.

Первый запуск: суперпользователь, Dashboard, GraphQL Playground

Создаём администратора:

docker compose exec api python3 manage.py createsuperuser

Введите email и пароль — под ними вы войдёте в Dashboard.

Открываем https://admin.example.com — это React-приложение, которое ходит на API по адресу, указанному в API_URI при сборке образа dashboard (обычно он берётся из переменной окружения API_URL в самом контейнере dashboard — добавьте её в compose-сервис dashboard, если панель не находит бэкенд):

  dashboard:
    image: ghcr.io/saleor/saleor-dashboard:latest
    environment:
      API_URL: "https://api.example.com/graphql/"

После логина проверьте GraphQL-эндпоинт напрямую: https://api.example.com/graphql/ в браузере откроет GraphiQL/Playground (при DEBUG=False playground по умолчанию отключён — это ожидаемо; для отладки временно включите PLAYGROUND_ENABLED=True в .env, но на проде держать его открытым не стоит).

Базовая проверка — создать тестовый товар через Dashboard (Catalog → Products → Create), убедиться, что он появляется в GraphQL-запросе:

query {
  products(first: 5, channel: "default-channel") {
    edges {
      node {
        name
        pricing {
          priceRange {
            start { gross { amount currency } }
          }
        }
      }
    }
  }
}

Если запрос возвращает пустой список — проверьте, что товар опубликован в нужном канале (channel) и указана цена, это частая причина «товар есть, а в API его нет».

Продакшен-нюансы: почта, медиафайлы, бэкапы

Почта. Без настроенного SMTP Saleor не отправит письма о заказе и восстановлении пароля. В .env добавьте:

EMAIL_URL=smtp://user:password@smtp.yourprovider.com:587/?tls=True

и перезапустите api и worker — письма шлёт именно воркер через Celery-задачи.

Медиафайлы. По умолчанию изображения товаров лежат в volume saleor-media на локальном диске — нормально для старта, но не масштабируется на несколько инстансов API и не переживает миграцию без ручного переноса volume. Для продакшена логичнее вынести хранилище во внешний S3-совместимый сервис (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_STORAGE_BUCKET_NAME, AWS_S3_ENDPOINT_URL в .env) — например, поднять свой MinIO рядом, как описано в статье MinIO в Docker Compose: готовый файл.

Бэкапы БД. Всё, что реально критично в Saleor, лежит в Postgres — каталог, заказы, клиенты. Простейший вариант с cron:

docker compose exec -T db pg_dump -U saleor saleor | gzip > /opt/backups/saleor-$(date +%F).sql.gz

Добавьте это в /etc/cron.d/saleor-backup с ежедневным запуском и ротацией старых архивов (find /opt/backups -mtime +14 -delete), и храните копию не только на этом же сервере — если диск умрёт, локальный бэкап не поможет.

Мониторинг ресурсов. Celery-воркер под нагрузкой (много вебхуков, синхронизация с внешними системами) съедает память рывками — держите на сервере запас RAM сверх расчётного минимума и следите за docker stats в первые недели после запуска, чтобы понять реальный профиль потребления именно вашего магазина.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Нужен ли отдельно storefront (витрина для покупателей)?

Да, Saleor Core и Dashboard — это API и админка. Витрину для покупателей вы либо берёте из официального Next.js Storefront (тоже разворачивается контейнером), либо пишете свою на GraphQL-клиенте — Saleor это допускает изначально.

Можно ли обойтись без Docker и поставить Saleor Core напрямую через pip/poetry?

Технически да, для разработки так и делают, но для продакшена вам всё равно придётся вручную поднимать Postgres, Redis, Celery worker и beat-планировщик отдельными процессами — Docker Compose просто избавляет от этой рутины и рассинхронизации версий.

Почему Dashboard открывается, но пишет ошибку соединения с API?

Чаще всего не совпадает API_URL в переменных окружения контейнера dashboard и реальный публичный адрес API, либо в ALLOWED_CLIENT_HOSTS/CORS-настройках API не добавлен домен админки.

Хватит ли 4 ГБ RAM для боевого магазина?

Для небольшого каталога и низкого трафика — как правило да, но с ростом числа воркеров Celery и параллельных GraphQL-запросов это становится узким местом одним из первых; проще сразу закладывать 8 ГБ, чем потом переезжать под нагрузкой.

Как обновлять Saleor до новой версии?

Меняете тег образа в docker-compose.yml для api, worker и dashboard, делаете docker compose pull, docker compose up -d, затем docker compose exec api python3 manage.py migrate — и обязательно смотрите changelog на breaking changes перед мажорным апгрейдом.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →