Run Django Management Commands in Docker and Kubernetes

Run Django manage.py commands against containerised deployments: docker compose exec versus run, kubectl exec versus one-off Jobs, loading the right settings and secrets, interactive shells, and keeping one-off commands out of web containers.

Problem statement

On a classic server you SSH in, activate the virtualenv, and run python manage.py shell. In containers it is less obvious: the code lives in an image, settings and secrets come from the container environment, and there may be several replicas. Running a command in the wrong way leads to errors such as missing environment variables, commands that run on a stale container, or a one-off data fix accidentally killing a production web container when it ends.

Where to run manage.pyexecin a running containerrun --rmnew one-off containerk8s Jobtracked, logged runSame image + envas the web serviceUse the same image and environment as the app, in a container meant for the task.
  1. Docker Compose, quick commands: docker compose exec web python manage.py <cmd> (uses the running container).
  2. Docker Compose, one-off tasks: docker compose run --rm web python manage.py <cmd> (fresh container, same image and env).
  3. Kubernetes, interactive: kubectl exec -it deploy/web -- python manage.py shell.
  4. Kubernetes, anything that changes data: a one-off Job from the same image and secrets, with logs and an exit status.
  5. Never run long or heavy commands inside a web pod serving traffic.
  6. Make sure DJANGO_SETTINGS_MODULE and secrets are present; they come from the container environment.

Step-by-step solution

1. Docker Compose: exec versus run

docker compose exec web python manage.py showmigrations
docker compose run --rm web python manage.py migrate --noinput
docker compose run --rm web python manage.py shell
  • exec starts a process inside the running web container. It is fast and shares that container's environment, but it competes with web traffic and dies if the container is replaced.
  • run --rm starts a new container from the service definition (same image, env files, networks, volumes), runs the command, and removes it. Use it for migrations, data fixes, and anything long.

run also starts dependencies like db unless you pass --no-deps.

2. Kubernetes: exec for quick looks

kubectl exec -it deploy/web -- python manage.py shell
kubectl exec deploy/web -- python manage.py check --deploy

deploy/web picks one pod of the Deployment. The pod already has the right image, environment, and secret mounts. Keep exec sessions for read-only inspection and short tasks.

3. Kubernetes: Jobs for real work

apiVersion: batch/v1
kind: Job
metadata: { name: backfill-slugs-20261002 }
spec:
  backoffLimit: 0
  ttlSecondsAfterFinished: 86400
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: task
          image: ghcr.io/you/myapp:SHA
          command: ["python", "manage.py", "backfill_slugs", "--batch-size", "500"]
          envFrom: [{ secretRef: { name: django-env } }]
          resources: { requests: { cpu: 250m, memory: 512Mi } }
kubectl apply -f backfill-job.yaml
kubectl logs -f job/backfill-slugs-20261002
kubectl wait --for=condition=complete job/backfill-slugs-20261002 --timeout=3600s

A Job has its own resources, logs, and a success or failure status, and does not disturb web pods. You can also clone the Deployment's pod spec quickly with kubectl create job --from=cronjob/<name> if you keep a suspended CronJob as a template.

pick the right way to run a commandRuns inUse forcompose execthe live web containerquick checks, shellcompose run --rm✓a fresh containermigrate, data fixeskubectl execone live podinspection, short tasksKubernetes Job✓its own pod✓anything that changes dataLong or data-changing commands get their own containerdjango-deployment.com
A Job leaves logs and an exit status you can review later.

4. Get the environment right

Commands fail with ImproperlyConfigured or connect to the wrong database when the environment differs from the web service. With Compose, run and exec both use the service's env_file and environment. A plain docker run myapp python manage.py ... does not; pass --env-file and the network explicitly, or use Compose. In Kubernetes, Jobs need the same envFrom and volume mounts as the Deployment.

5. Interactive shells safely

Production shells can change data instantly. Prefer read-only checks, wrap changes in transaction.atomic() so you can inspect before committing, and record what you ran. For repeated fixes, write a management command, review it, and run it as a Job.

one-off commands, done safelySame image tag as productionno version skewSame env and secrets as websettings and database matchFresh container or Job for changesnot a serving podLogs and exit status keptauditableHeavy commands inside web podssteal capacity, die on rolloutTreat one-off commands as small deployments of their owndjango-deployment.com
Pin the image tag in Jobs so a rerun executes the same code.

Verification check: docker compose run --rm web python manage.py check --deploy and kubectl exec deploy/web -- python manage.py check --deploy both pass, proving the environment loads; a sample Job completes and its logs are retrievable with kubectl logs job/<name>.

Explanation

A container image contains code and dependencies; the runtime supplies configuration through environment variables, mounted secrets, and networks. Running a management command correctly means reproducing that runtime context. Compose and Kubernetes both can do this for you if you start the command through the same service or pod definition. Separating one-off work into its own container keeps it from competing with request handling and from being killed when a rolling update replaces the pod it was running in.

Edge cases / notes

  • Distroless images have no shell; run python manage.py directly as the command, without sh -c.
  • Non-root users in the image may lack write access to paths that commands assume, such as /tmp in read-only root filesystems; mount an emptyDir.
  • TTY. Use -it only for interactive commands; scripts in CI should not allocate a TTY.
  • Celery beat images may share the same image; commands can run from any container built from it.

Next steps

FAQ

What is the difference between docker compose exec and run?

exec runs inside the running container; run --rm starts a fresh container from the same service definition.

How do I open a Django shell in Kubernetes?

kubectl exec -it deploy/web -- python manage.py shell.

Where should data migrations or backfills run?

In a one-off container or Kubernetes Job with the production image and environment.

Why does manage.py fail with missing settings in my container?

The command does not have the service's environment. Run it through Compose or the pod spec, not a bare docker run.

Keep going