django-planet
March 20, 2023

Single host Django and Docker deployment

in blog Screaming At My Screen
original entry Single host Django and Docker deployment

One of the best ways to deploy a Django project while maintaining some sanity is Docker. A single, beefy VM or bare metal host will likely bring you a long way when just starting a new project or while working on a side project that does not have to scale to infinity and beyond. Usually you build a Docker image, upload it to a container registry and deploy from the registry. This results in a few more moving parts than I am okay with for a quick side project or something small (like this blog).

For this to work you either run a container registry yourself, or trust a third party with keeping your images secure and private. One requires trust in third parties, the other means more work and time for something you can spend on more fun things like your actual project. So let us skip this step.

I am using this blog as an example. In the spirit of staying true to "tech quips" I will use an early, working iteration of the deployment script. I will add some notes at the bottom what you should consider doing and what I already changed.

The Dockerfile itself is as barebones as it gets.

FROM python:3.10-slim-bullseye

COPY . .
RUN chmod +x entrypoint.sh
RUN pip install -r requirements.txt

EXPOSE 8000

ENTRYPOINT ["/entrypoint.sh"]

When building the image I tag it for my internal registry if I ever decide to store images.

docker build -t registry.home.arpa/sams .

And now to the actual deployment

docker save registry.home.arpa/sams > /tmp/sams.tar
scp /tmp/sams.tar chonker1-sams:~
rm /tmp/sams.tar
ssh chonker1-sams 'docker load < /home/sams/sams.tar'
ssh chonker1-sams 'rm /home/sams/sams.tar'
ssh chonker1-sams 'docker stop sams'
ssh chonker1-sams 'docker rm sams'
ssh chonker1-sams 'docker run -d -t -v /home/sams/db.sqlite3:/db.sqlite3 -p 8001:8000 --restart always --name sams --security-opt apparmor=unconfined registry.home.arpa/sams:latest'
ssh chonker1-sams 'docker image prune --all --force'

Breaking it down

  • save the image to a tar archive
  • upload it to the host
  • load the image
  • stop the old container and start a new one
  • clean up

And a few things to know

  • the Dockerfile is not cleaned up: I am most likely using the wrong base image if I would be optimising for size or build time and I definitely should not throw all files in /.
  • you should likely tag the image properly and not just replace the existing one
  • by now a proper apparmor profile is deployed, this is the only change I actually made so far
  • I configured the host chonker1-sams in my .ssh/config to not specify the host, username and key file in every command
  • yes, I named my bare metal servers chonkerX

I would say this approach is a pretty good starting point for a "minimum viable deployment strategy" and so far it works well for multiple projects.