Пошаговая инструкция · Самостоятельная установка
Как установить Saleor и Next.js Storefront на сервер Ubuntu
Пошаговая инструкция для самостоятельной установки. Развернём Saleor API, административную панель и production-витрину Next.js на одном сервере. Создадим тестовый товар, проверим изображение и корзину, сохраним базу, медиа и ключи для восстановления.
Что подготовить и как читать инструкцию #
Saleor — сервер электронной торговли с GraphQL API. Dashboard управляет каталогом и заказами, а Next.js Storefront показывает товары покупателю. Дополнительно нужны PostgreSQL, очередь Valkey, Celery worker и один планировщик Beat. Пример рассчитан на небольшой магазин на одном сервере; отказ этого VPS остановит все компоненты.
Подготовьте Ubuntu 26.04 LTS, Docker Engine, Compose v2, Nginx, git и openssl по общей инструкции. Для сборки витрины на том же сервере стартуйте с 4–8 vCPU и 12–16 ГБ памяти. Каталог, обработка изображений и число одновременных корзин определяют дальнейшие ресурсы. База и медиа должны храниться постоянно, а резервные копии — вне этого сервера.
Команды выполняйте в терминале сервера после подключения по SSH, если шаг не указывает другое место. sudo даёт команде права администратора. Действия в браузере выполняйте на своём компьютере. Домены, IP-адреса и значения ВАШ_ПАРОЛЬ из примеров заменяйте своими до выполнения команды.
Двигайтесь по одному шагу и проверяйте результат перед следующим. Блок с <<EOF или другим именем завершения копируйте целиком: он записывает содержимое одного файла. При работе в nano сохраняйте файл клавишами Ctrl+O, Enter и выходите Ctrl+X. Если прервали SSH-сеанс, снова перейдите в указанный каталог; переменные предыдущего сеанса потребуется задать заново.
Настройка внешнего доступа использует обратный прокси Nginx и Certbot для Nginx. Приложение остаётся доступным на локальном порту; снаружи открыт его HTTPS-домен.
Дополнительные официальные источники: Миграции и первый администратор, Релиз API 3.23.39, Production-шаблон Paper, Каналы магазина, Товары и варианты, Dockerfile API релиза 3.23.39.
Шаг 1. Подготовьте каталог магазина #
В /srv/saleor будут конфигурация API, файлы магазина и исходники витрины. Создадим каталог и дадим владельцу SSH-сеанса право работать с ним.
sudo mkdir -p /srv/saleor/media
sudo chown -R "$USER":"$USER" /srv/saleor
chmod 755 /srv/saleor /srv/saleor/media
cd /srv/saleorТекущий каталог — /srv/saleor.
Шаг 2. Создайте серверный ключ JWT #
Создаём закрытый RSA-ключ. Он используется сервером для подписания токенов и должен сохраняться при восстановлении.
umask 077
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out jwt.pemjwt.pem создан; его содержимое не передавайте в браузер или публичный репозиторий.
Шаг 3. Сохраните секреты приложения в .env #
В .env запишем независимые секреты и подготовленный RSA-ключ. Небольшой Python-скрипт только переводит ключ в нужное для окружения представление; chmod закрывает файлы от посторонних. SMTP-адреса пока примеры: на следующем шаге заменим их реальными реквизитами. Генератор предназначен только новой установке.
.envcat > .env <<EOF
POSTGRES_PASSWORD=$(openssl rand -hex 32)
SECRET_KEY=$(openssl rand -hex 48)
REVALIDATE_SECRET=$(openssl rand -hex 32)
ORDER_VIEW_SECRET=$(openssl rand -hex 32)
EMAIL_URL=smtp+tls://replace-user:replace-password@smtp.example.com:587
USER_EMAIL_URL=smtp+tls://replace-user:replace-password@smtp.example.com:587
DEFAULT_FROM_EMAIL=shop@example.com
DEFAULT_COUNTRY=RU
DEFAULT_CURRENCY=RUB
EOF
python3 - <<'KEY'
from pathlib import Path
with Path('.env').open('a') as f:
f.write("RSA_PRIVATE_KEY='" + Path('jwt.pem').read_text() + "'\n")
KEY
chmod 600 .env jwt.pemСоздан .env, JWT-настройка заполнена; права .env и jwt.pem — 600.
Шаг 4. Заполните адреса отправки почты #
Откройте .env. Измените EMAIL_URL, USER_EMAIL_URL и DEFAULT_FROM_EMAIL по данным своего SMTP; специальные символы логина/пароля внутри URL требуют кодирования. Сохраните созданные ключи.
nano .envВ SMTP нет replace-user/replace-password; отправитель ваш.
Шаг 5. Скачайте закреплённую витрину #
Получим официальный Storefront Paper и переключим его на выбранную ревизию. Файлы витрины — отдельный компонент от API Saleor.
git clone https://github.com/saleor/storefront.git storefront
git -C storefront checkout b73bdce3269cceb08feff856af5067d117c79cb6Исходники находятся в storefront, выбран указанный коммит.
Шаг 6. Сохраните описание всех компонентов #
Откройте nano compose.yaml и вставьте YAML. API обслуживает GraphQL, worker/beat обрабатывают задания, dashboard — управление магазином, storefront — покупательскую витрину. Замените домены примера во всём файле.
x-saleor: &saleor
image: ghcr.io/saleor/saleor:3.23.39
restart: unless-stopped
env_file: .env
environment: &saleor-env
DEBUG: "False"
ALLOWED_HOSTS: saleor-api.example.com,api,localhost,127.0.0.1
ALLOWED_CLIENT_HOSTS: shop.example.com,saleor-admin.example.com
ALLOWED_GRAPHQL_ORIGINS: https://shop.example.com,https://saleor-admin.example.com
PUBLIC_URL: https://saleor-api.example.com/
DASHBOARD_URL: https://saleor-admin.example.com/dashboard/
MEDIA_URL: https://saleor-api.example.com/media/
DATABASE_URL: postgres://saleor:${POSTGRES_PASSWORD}@db:5432/saleor
CACHE_URL: redis://cache:6379/0
CELERY_BROKER_URL: redis://cache:6379/1
PLAYGROUND_ENABLED: "False"
volumes:
- ./media:/app/media
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
services:
db:
image: postgres:15-alpine
restart: unless-stopped
environment:
POSTGRES_DB: saleor
POSTGRES_USER: saleor
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set password}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: [CMD-SHELL, "pg_isready -U saleor -d saleor"]
interval: 10s
timeout: 5s
retries: 10
cache:
image: valkey/valkey:8.1-alpine
restart: unless-stopped
command: [valkey-server, --appendonly, "yes", --appendfsync, everysec]
volumes:
- cachedata:/data
api:
<<: *saleor
ports:
- "127.0.0.1:8000:8000"
worker:
<<: *saleor
command: [celery, --app, "saleor.celeryconf:app", worker, -E, --loglevel=info, --concurrency=2]
beat:
<<: *saleor
command: [celery, --app, "saleor.celeryconf:app", beat, --scheduler, saleor.schedulers.schedulers.DatabaseScheduler]
dashboard:
image: ghcr.io/saleor/saleor-dashboard:3.23.39
restart: unless-stopped
ports:
- "127.0.0.1:9002:80"
environment:
API_URL: https://saleor-api.example.com/graphql/
APP_MOUNT_URI: /dashboard/
storefront:
image: saleor-paper-local:b73bdce
build:
context: ./storefront
dockerfile: Storefront.Dockerfile
args:
NEXT_PUBLIC_SALEOR_API_URL: https://saleor-api.example.com/graphql/
NEXT_PUBLIC_STOREFRONT_URL: https://shop.example.com
NEXT_PUBLIC_DEFAULT_CHANNEL: default-channel
restart: unless-stopped
ports:
- "127.0.0.1:3011:3000"
environment:
NODE_ENV: production
HOSTNAME: 0.0.0.0
NEXT_PUBLIC_SALEOR_API_URL: https://saleor-api.example.com/graphql/
NEXT_PUBLIC_STOREFRONT_URL: https://shop.example.com
NEXT_PUBLIC_DEFAULT_CHANNEL: default-channel
REVALIDATE_SECRET: ${REVALIDATE_SECRET:?set secret}
ORDER_VIEW_SECRET: ${ORDER_VIEW_SECRET:?set secret}
ALLOW_DUMMY_PAYMENT: "false"
NEXT_PUBLIC_ALLOW_DUMMY_PAYMENT: "false"
volumes:
pgdata:
cachedata:Сохранён Compose, внутренние порты привязаны к localhost.
Шаг 7. Проверьте конфигурацию #
Проверим YAML и переменные до запуска. Витрину будем собирать после появления работающего HTTPS API.
docker compose config --quietconfig --quiet завершился без ошибки.
Шаг 8. Скачайте готовые компоненты #
Загрузим API, панель, базу и очередь. Storefront — локальная сборка из исходников, поэтому её здесь ещё не скачиваем.
docker compose pull api worker beat dashboard db cachepull завершился успешно.
Шаг 9. Запустите базу и кеш #
База и кеш нужны миграции. После создания PostgreSQL не меняйте POSTGRES_PASSWORD только в .env: пароль существующей базы автоматически не изменится.
docker compose up -d db cachedb и cache запущены.
Шаг 10. Создайте таблицы Saleor #
Штатная migrate подготавливает структуру новой базы. Выполняется одноразовый контейнер API и удаляется после окончания.
docker compose run --rm api python3 manage.py migrateМиграции завершились без ошибки.
Шаг 11. Создайте администратора магазина #
createsuperuser интерактивно спросит email и пароль. Это административная запись магазина, а не пароль базы.
docker compose run --rm api python3 manage.py createsuperuserАдминистратор создан; реквизиты сохранены.
Шаг 12. Запустите API и панель #
Запустим основные компоненты и посмотрим журнал обработки. Витрина пока отсутствует — это нормальный промежуточный результат.
docker compose up -d api worker beat dashboard
docker compose logs --tail=80 api worker beatAPI, worker, beat и dashboard работают без повторных ошибок.
Шаг 13. Сохраните сайт Nginx #
Установите Nginx и Certbot по подготовке Ubuntu, если ещё не установили. Откройте sudo nano /etc/nginx/sites-available/saleor-nextjs и вставьте файл ниже. Замените все домены своими. Замените 203.0.113.10 внешним IP своего рабочего компьютера: ограничение действует только на панель, а проверка сертификата остаётся доступной. Nginx передаёт приложению домен, адрес клиента и протокол; Upgrade и Connection сохраняют WebSocket. До получения сертификата условие $scheme блокирует обычные HTTP-запросы кодом 403, чтобы вход не происходил без шифрования. /media/ выдаётся из /srv/saleor/media/ с сохранением соответствия пути; Nginx должен иметь право чтения файлов. Пока storefront ещё не запущен, его HTTPS-сайт может отвечать 502; сертификат всё равно можно получить.
/etc/nginx/sites-available/saleor-nextjsmap $http_upgrade $saleor_nextjs_connection_upgrade {
default upgrade;
"" close;
}
server {
listen 80;
server_name saleor-api.example.com;
client_max_body_size 100m;
location /media/ {
if ($scheme = http) { return 403; }
alias /srv/saleor/media/;
}
location / {
if ($scheme = http) { return 403; }
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $saleor_nextjs_connection_upgrade;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
}
server {
listen 80;
server_name saleor-admin.example.com;
client_max_body_size 100m;
location / {
if ($scheme = http) { return 403; }
allow 203.0.113.10;
deny all;
proxy_pass http://127.0.0.1:9002;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $saleor_nextjs_connection_upgrade;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
}
server {
listen 80;
server_name shop.example.com;
client_max_body_size 100m;
location / {
if ($scheme = http) { return 403; }
proxy_pass http://127.0.0.1:3011;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $saleor_nextjs_connection_upgrade;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
}
Сохраните Ctrl+O, Enter, Ctrl+X. В файле пока нет путей к сертификатам: Nginx сможет проверить его до выпуска HTTPS.
Шаг 14. Подключите сайт и проверьте HTTP #
Ссылка подключает сохранённый сайт. nginx -t проверяет все настройки сервера; && разрешает перечитать их только при успешной проверке. curl проверяет ответ для вашего домена. При ошибке остановитесь, исправьте указанную строку и повторите проверку.
sudo ln -sfn /etc/nginx/sites-available/saleor-nextjs /etc/nginx/sites-enabled/saleor-nextjs
sudo nginx -t && sudo systemctl reload nginx
curl -I http://saleor-api.example.com/Ожидается successful от nginx -t и HTTP 403 для обычного запроса: до HTTPS это намеренная блокировка. DNS должен указывать на сервер, TCP 80/443 — быть доступен у провайдера. Ничего не вводите в форму входа по HTTP.
Шаг 15. Получите сертификат и включите HTTPS #
Каждое имя из команды должно указывать на этот сервер. Certbot проверит домены, сам добавит сертификат в сайт Nginx и включит перенаправление HTTP → HTTPS. Укажите свою почту при запросе и подтвердите условия Let’s Encrypt. Это отдельное действие после успешной проверки HTTP.
sudo /snap/bin/certbot --nginx -d saleor-api.example.com -d saleor-admin.example.com -d shop.example.com --redirectCertbot сообщил об успешной установке. Откройте HTTPS-адрес со своего компьютера: браузер не должен предупреждать о сертификате. Для закрытой панели используйте разрешённый IP. При ошибке исправьте DNS, в том числе AAAA, либо доступ к TCP 80; не повторяйте запросы выпуска подряд.
Шаг 16. Проверьте продление сертификата #
Certbot из snap создаёт службу автоматического продления. Dry-run выполняет пробное продление: настоящие сертификаты не заменяются. Затем проверьте, что таймер планирует следующий запуск. Сохраняйте доступ к TCP 80 для следующих проверок домена.
sudo /snap/bin/certbot renew --dry-run
systemctl list-timers --all 'snap.certbot.renew*'Пробное продление успешно, у таймера есть следующий запуск. Если тест не прошёл, проверьте /var/log/letsencrypt/letsencrypt.log; сначала исправьте причину. После этого можно создавать учётную запись и приглашать пользователей.
Шаг 17. Сохраните Dockerfile витрины #
Создайте nano storefront/Storefront.Dockerfile и вставьте шаблон. Он ставит зависимости из lock-файла и собирает standalone Next.js. В storefront/.dockerignore добавьте .env*, node_modules и .next; рабочие секреты не должны попасть в build context.
Dockerfile витриныFROM node:24-alpine AS builder
RUN apk add --no-cache libc6-compat
RUN corepack enable && corepack prepare pnpm@10.28.1 --activate
WORKDIR /app
COPY . .
RUN pnpm install --frozen-lockfile
ARG NEXT_PUBLIC_SALEOR_API_URL
ARG NEXT_PUBLIC_STOREFRONT_URL
ARG NEXT_PUBLIC_DEFAULT_CHANNEL=default-channel
ENV NEXT_PUBLIC_SALEOR_API_URL=${NEXT_PUBLIC_SALEOR_API_URL}
ENV NEXT_PUBLIC_STOREFRONT_URL=${NEXT_PUBLIC_STOREFRONT_URL}
ENV NEXT_PUBLIC_DEFAULT_CHANNEL=${NEXT_PUBLIC_DEFAULT_CHANNEL}
ENV NEXT_TELEMETRY_DISABLED=1 NEXT_PUBLIC_ALLOW_DUMMY_PAYMENT=false
RUN NEXT_OUTPUT=standalone pnpm build
FROM node:24-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production HOSTNAME=0.0.0.0 PORT=3000
RUN addgroup -S -g 1001 app && adduser -S -u 1001 -G app app
COPY --from=builder --chown=app:app /app/public ./public
COPY --from=builder --chown=app:app /app/.next/standalone ./
COPY --from=builder --chown=app:app /app/.next/static ./.next/static
RUN mkdir -p .next/cache && chown -R app:app .next
USER app
CMD ["node", "server.js"]Dockerfile сохранён; в папке исходников нет рабочих .env с секретами.
Шаг 18. Соберите и запустите витрину #
Сборка обращается к HTTPS GraphQL, поэтому API должен уже отвечать. Публичный API-адрес входит в браузерную сборку: при его изменении понадобится новая сборка.
docker compose build storefront
docker compose up -d storefrontСборка успешна; shop.example.com открывает витрину.
Шаг 19. Подготовьте тестовый товар #
В Dashboard создайте канал продаж с идентификатором из настроек витрины, склад, товар с вариантом, ценой и остатком. Опубликуйте товар для этого канала.
Товар виден в витрине с ценой и доступным вариантом.
Шаг 20. Проверьте тестовый заказ #
Добавьте товар в корзину, настройте доставку и выбранный платёжный модуль по его документации. Проводите проверку в тестовом режиме провайдера, не включайте фиктивную оплату для рабочего магазина.
Тестовый заказ появляется в Dashboard, меняет остаток по выбранным правилам и доставляет письмо покупателю.
Шаг 21. Остановите запись перед копией #
Создадим закрытый каталог и остановим API, фоновые задания и витрину; база останется доступной. Предупредите о перерыве продаж.
cd /srv/saleor
umask 077
backup_dir="$HOME/backups/saleor/$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$backup_dir"
docker compose stop api worker beat storefrontКомпоненты остановлены, backup_dir задан в этом SSH-сеансе.
Шаг 22. Скопируйте базу и файлы #
Дамп содержит каталог и заказы, архив — media, ключи и конфигурацию. Не сохраняем пересоздаваемые node_modules и .next. Перенесите обе части во внешнее закрытое хранилище.
docker compose exec -T db pg_dump -U saleor saleor > "$backup_dir/database.sql"
tar --exclude=storefront/node_modules --exclude=storefront/.next \
-czf "$backup_dir/files.tar.gz" media .env jwt.pem compose.yaml storefrontОба файла созданы без ошибки; копия содержит базу и исходники/настройки витрины.
Шаг 23. Верните магазин и проверьте копию #
Запустите сервисы. Восстановление проверяйте на отдельном сервере с тестовыми SMTP и оплатой: оно не должно отправлять повторные рабочие письма или платежи.
docker compose start api worker beat storefrontТестовый товар и заказ доступны после запуска и восстановления.
Если результат шага отличается #
Вернитесь к проверке того шага, на котором появилась ошибка. Первая ошибка в выводе обычно полезнее последних строк: последующие команды могли не получить нужный файл или работающую службу. Не повторяйте установку целиком поверх частично настроенного приложения.
Если страница не открывается, сначала проверьте домен и входящие порты, затем состояние приложения и его журнал. Если сайт открывается, но отдельное действие не работает, проверьте связанные компоненты — обработчик фоновых задач, почту или права пользователя. Официальные источники в конце статьи относятся к выбранному способу установки.
Настройки внешнего доступа храните вместе с копией приложения: /etc/nginx/sites-available/saleor-nextjs и /etc/letsencrypt содержат сайт и сертификаты с закрытыми ключами. Архивируйте их с sudo в закрытое хранилище. После восстановления подключите сайт через sites-enabled, выполните sudo nginx -t, затем перечитайте Nginx. Для ошибки 502 сначала проверьте локальное приложение и /var/log/nginx/error.log; для сертификата — /var/log/letsencrypt/letsencrypt.log и таймер продления.