Tech

Dockerizing III

The third step of my Docker migration is happening on the production VPS, but it is not a live traffic cutover yet. The goal is to prepare the server carefully, validate the configuration, and keep the existing Gunicorn and Nginx deployment available while Docker is tested alongside it.

Docker access comes first

After installing Docker, my first container command failed with a permission error for /var/run/docker.sock. Installing Docker does not automatically mean that the current Linux user can control the Docker daemon.

The deployment user must belong to the docker group, and the socket must be owned by that group. This is worth checking explicitly:

id -nG
ls -l /var/run/docker.sock
docker ps

Membership in the Docker group effectively grants root-level control over the server, so it should be limited to trusted deployment users.

Production configuration stays outside Git

The container receives its Django, PostgreSQL, and S3 configuration from a server-only dotenv file:

cp .env.docker.example .env.docker
chmod 600 .env.docker
nano .env.docker

The file contains real production values, so it is ignored by Git and excluded from the Docker build context. Django debug mode is disabled, HTTPS security settings are enabled, and only the production domains are trusted.

One small but important difference is the database hostname. In the old host-based deployment, Django can connect to PostgreSQL through localhost. Inside a bridge-network container, localhost means the container itself, so the host database needs a container-reachable address and a tightly scoped PostgreSQL rule.

Static files need the correct owner

The application image runs as an unprivileged user with UID 10001. The production Compose configuration bind-mounts the existing static directory so Django can collect files there and Nginx can continue serving them from the host.

mkdir -p static
sudo chown -R 10001:10001 static
sudo chmod -R u=rwX,go=rX static

This avoids running the application as root and avoids changing the static-file architecture during the same deployment.

Validate before starting anything

Before building or starting a container, Docker Compose can validate the merged base and production files:

docker compose \
  --env-file .env.docker \
  -f compose.yaml \
  -f compose.production.yaml \
  config --quiet

The --quiet option is useful on a production server because it checks the configuration without printing expanded environment values to the terminal.

This step also produced a simple deployment lesson: infrastructure files must be committed before the server can use them. A valid local Compose file is irrelevant if it was never included in the revision pulled by the VPS.

What comes next

Once Docker access, secrets, database connectivity, and static permissions are correct, I can build the image and start it on 127.0.0.1:8000. I will test its health endpoint and application pages directly before changing Nginx.

The existing Gunicorn service remains the rollback path. Only after the container is healthy will Nginx be pointed at it, and the GitHub Actions deployment will be updated in a later step.

#Deploy #Docker #Learning
Share on Reddit