Saleor is a serious, GraphQL-native e-commerce platform written in Python/Django. It powers real storefronts at scale, and because it's API-first you can put any frontend in front of it. The trade-off is that Saleor is *not* a single container — a production deployment is a small constellation of services. This guide explains what each piece does and how to run them.
Saleor's architecture
A minimal but production-shaped Saleor deployment includes:
| Component | Role |
|---|---|
| Saleor API (Django/ASGI) | The GraphQL core, served via uvicorn/gunicorn |
| PostgreSQL | Primary datastore (Saleor recommends a recent major version) |
| Redis | Celery broker + cache |
| Celery worker | Async tasks: emails, webhooks, exports |
| Object storage (S3) | Product media and static files |
| Saleor Dashboard | Optional React admin UI (static site) |
The important mental model: the API and the Celery worker run the *same codebase* but with different start commands, and they share the database and Redis.
Step 1: Prepare configuration
Saleor is configured almost entirely through environment variables. The critical ones:
DATABASE_URL=postgres://user:pass@host:5432/saleor
REDIS_URL=redis://host:6379/0
CELERY_BROKER_URL=redis://host:6379/1
SECRET_KEY=<long random string>
ALLOWED_HOSTS=api.yourstore.com
ALLOWED_CLIENT_HOSTS=yourstore.com
DEFAULT_FROM_EMAIL=noreply@yourstore.com
# S3-compatible media
AWS_STORAGE_BUCKET_NAME=saleor-media
AWS_MEDIA_BUCKET_NAME=saleor-media
AWS_S3_ENDPOINT_URL=https://...Saleor ships an official Docker image, which makes deployment far more predictable than building from source.
Step 2: Run the database migrations and populate data
Saleor needs migrations applied and, on first boot, you typically want to populate the database. Run a one-off command:
python manage.py migrate
# optional: load demo data for a first look
python manage.py populatedb --createsuperuserIn a container platform, run this as a one-off job or as a release/pre-deploy command so it executes once per deploy rather than per replica.
Step 3: Deploy the API
Use Saleor's official image. The API runs as an ASGI app:
FROM ghcr.io/saleor/saleor:3.20
# the image already has a sane entrypointThe start command for the web service:
gunicorn saleor.asgi:application -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000On PandaStack, deploying any Dockerfile is first-class. Create a container app, point it at your repo (or the official image), and set the start command above. Bind to the injected PORT.
Step 4: Deploy the Celery worker
The worker is the same image with a different command — no GraphQL, just background processing:
celery -A saleor --app=saleor.celeryconf:app worker --loglevel=infoCreate a second container app from the same image with this start command. It has no public HTTP endpoint — it just connects to Redis and Postgres. Both the API and worker must share the same DATABASE_URL, REDIS_URL, and SECRET_KEY.
Step 5: Wire up data services
This is where a platform with managed databases saves real time. On PandaStack:
- 1Provision a managed PostgreSQL instance. Saleor is happy on PostgreSQL 16.x.
- 2Provision a managed Redis instance (used for both cache and the Celery broker).
- 3Link both to the API app and worker app. The connection strings are injected as env vars.
For media, point Saleor at S3-compatible storage. A self-hosted MinIO bucket works perfectly via AWS_S3_ENDPOINT_URL.
Step 6: Deploy the Dashboard (optional)
The Saleor Dashboard is a static React app. Build it pointing at your API:
API_URL=https://api.yourstore.com/graphql/ npm run buildDeploy the build/ output as a static site. PandaStack auto-detects the framework and serves it with automatic SSL and a CDN. Static sites are unlimited on Pro and Premium plans.
Scaling and resource notes
- The API is CPU-bound under GraphQL load; the worker is bursty. Scale them independently — that's the whole point of splitting them.
- Saleor's GraphQL schema is large; give the API enough memory. Start on a small compute tier and watch metrics before sizing up.
- Use database connection pooling. Many Django replicas times Saleor's connection count can exhaust Postgres
max_connections.
Honest caveats
Saleor is a heavyweight platform. If you're building a tiny shop, it's overkill — something like Medusa or even a hosted SaaS may serve you better. Saleor shines when you need a robust GraphQL API, multi-channel commerce, and deep customization. Budget for the operational reality of running an API plus a worker plus two data stores.
Wrapping up
Saleor's deployment complexity is really just "one app, two roles, two data stores." Once you internalize that, it's straightforward: API container, worker container, managed Postgres, managed Redis, S3 media, static Dashboard.
PandaStack's managed PostgreSQL and Redis plus first-class Dockerfile support make this constellation easy to wire together — and the free tier is enough to stand up a dev instance. Try it at https://dashboard.pandastack.io.
References
- Saleor documentation: https://docs.saleor.io/
- Saleor deployment docs: https://docs.saleor.io/setup/deployment
- Saleor Docker images: https://github.com/saleor/saleor/pkgs/container/saleor
- Saleor Dashboard: https://github.com/saleor/saleor-dashboard
- Celery documentation: https://docs.celeryq.dev/en/stable/