Deploy Saleor to Production

Deploy the Saleor headless e-commerce platform, built on Django and GraphQL, to production: run the API container with PostgreSQL and Redis, configure Celery workers, media storage, secrets, allowed hosts and CORS for the dashboard and storefront, and run migrations on upgrade.

Problem statement

Saleor is a headless commerce platform: a Django and GraphQL API (saleor), a React dashboard for staff, and your own storefront. The quick-start Docker setup is fine for trying it, but production needs persistent PostgreSQL, Redis, Celery workers for emails, webhooks, and imports, durable media storage, correct allowed hosts and client origins for the dashboard and storefront, and a migration step for every upgrade.

Saleor in productionAPI containerDjango + GraphQLCelery workersemails, webhooksPostgres + Redisdata, brokerDashboard + storeseparate originsThe API is a Django app; the dashboard and storefront are clients on other origins.
  1. Run the official ghcr.io/saleor/saleor image (pinned to a version) with Gunicorn or Uvicorn as provided.
  2. Provide PostgreSQL and Redis; set DATABASE_URL, CELERY_BROKER_URL, CACHE_URL, and SECRET_KEY.
  3. Run a Celery worker from the same image for background jobs.
  4. Configure ALLOWED_HOSTS, ALLOWED_CLIENT_HOSTS, and the public URL settings for the API, dashboard, and storefront domains.
  5. Store media on S3-compatible storage, and run python manage.py migrate before starting a new version.

Step-by-step solution

1. Choose versions and images

Saleor releases the API, dashboard, and storefront separately. Pin each to a release and upgrade them together following the release notes:

services:
  api:
    image: ghcr.io/saleor/saleor:3.21
    env_file: saleor.env
    depends_on: [db, redis]
    restart: unless-stopped
  worker:
    image: ghcr.io/saleor/saleor:3.21
    command: celery -A saleor.celeryconf:app worker -E --loglevel=info
    env_file: saleor.env
    restart: unless-stopped
  dashboard:
    image: ghcr.io/saleor/saleor-dashboard:3.21
    restart: unless-stopped
  db:
    image: postgres:16
    volumes: [pgdata:/var/lib/postgresql/data]
  redis:
    image: redis:7
volumes: { pgdata: {} }

Use the version numbers of the current release you choose; the example tags only illustrate pinning.

2. Configure the API

SECRET_KEY=<long random value>
DATABASE_URL=postgres://saleor:...@db:5432/saleor
CELERY_BROKER_URL=redis://redis:6379/1
CACHE_URL=redis://redis:6379/0
ALLOWED_HOSTS=api.shop.example.com
ALLOWED_CLIENT_HOSTS=dashboard.shop.example.com,shop.example.com
PUBLIC_URL=https://api.shop.example.com/
DEFAULT_FROM_EMAIL=shop@example.com
EMAIL_URL=smtp://user:pass@smtp.example.com:587/?tls=True

Variable names come from Saleor's settings module and occasionally change between versions; check the configuration reference for the version you deploy. ALLOWED_CLIENT_HOSTS controls which origins may receive links in emails such as password resets, and PUBLIC_URL is used to build absolute URLs.

what runs whereOriginTalks toAPI (Django)api.shop.example.comPostgres, Redis, S3Celery workerinternalRedis, PostgresDashboarddashboard.shop...API over GraphQLStorefrontshop.example.comAPI over GraphQLOne Django API serves two separate frontendsdjango-deployment.com
Both frontends are static or Node apps; only the API and worker need Django settings.

3. Media and static files

Product images go through Django storage. Configure S3-compatible storage through Saleor's settings variables (bucket, region, credentials, and custom domain), and serve images through a CDN. Saleor generates thumbnails on demand and stores them, so the storage must be writable by the API. Static files for the GraphQL playground and admin pages are collected into the image.

4. Migrations and first setup

Run migrations before each new version starts:

docker compose run --rm api python manage.py migrate
docker compose run --rm api python manage.py createsuperuser

For a demo catalog, populatedb exists, but never run it against production. Plan upgrades: some Saleor releases include long data migrations; test them on a copy of production first.

5. Webhooks, apps, and workers

Saleor's apps and webhooks deliver events asynchronously through Celery. Without a running worker, emails do not send, webhooks are delayed, and some exports never finish. Scale workers for your order volume, and monitor queue length. Put the API behind a reverse proxy with HTTPS, and give the dashboard its API URL at build or runtime according to its documentation.

version upgrade order1Read release notesbreaking changes2Restore prod copy to stagingtest migrations3Back up databasebefore upgrade4Migrate, then start APIsame version for worker5Update dashboardmatching versionAPI, worker, and dashboard versions move togetherdjango-deployment.com
Long data migrations may need a maintenance window on large catalogs.

Explanation

Saleor is a large Django project packaged as a container. Everything that applies to Django in production applies here: environment-based settings, PostgreSQL, a shared cache, Celery for background work, and object storage for uploads. What is specific is the split architecture: the API is the only component with database access, while the dashboard and storefront are separate frontends on their own origins, which is why host and client-origin settings matter.

Edge cases / notes

  • Saleor Cloud. If you do not want to operate the stack, Saleor offers a hosted version; self-hosting gives control at the cost of operations work.
  • GraphQL limits. Expensive queries can load the API heavily; use query cost limits and caching in the storefront.
  • Payments. Payment gateways run as Saleor apps; their webhooks need the API to be publicly reachable over HTTPS.

Next steps

FAQ

Is Saleor a Django app?

Yes. The Saleor API is a Django project exposing a GraphQL API.

Do I need Celery for Saleor?

Yes, for emails, webhooks, exports, and other background work.

Where should product images be stored?

In S3-compatible object storage, served through a CDN.

How do I upgrade Saleor?

Follow the release notes, test migrations on a production copy, back up, migrate, and upgrade the API, worker, and dashboard together.

Keep going