Django people who deploy to a VPS: What is your current setup?

I've always used Ubuntu, Postgres, gunicorn, and nginx. But I'm curious how people have deviated from that approach.

Mostly thinking about small to medium projects, which run from a single server.

#python #django

Thanks for all the replies everyone! It's really helpful to see the range of what people are using.

For anyone curious, I'll share the django-simple-deploy plugin that comes out of this when it's working. (There's room for more than one VPS-based plugin, it's almost certainly not a good idea to cram all these options into one plugin.)

And if you're just coming across this thread, please feel free to add your thoughts!

@ehmatthes Docker Compose + Caddy | Traeffik (probably moving everything to Caddy) with Cloudfront in front.

I also run Tailscale to access them with everything port closed except for 443 for SSL.

@ehmatthes I lean heavily on a managed postgres instance vs. hosting my own in docker. There is virtually no ceiling when running only web + worker nodes on a VPS.

It's also easier to scale up to a different provider if you ever want to move the box.

@webology Do you know of a good writeup of a Docker-based approach? I've used Docker-based PAAS services, but I've never used Docker on a bare server.

Also I completely agree about using a managed db instance.

@ehmatthes It's been a while for me. DO used to have good/decent write-ups for parts of this.

I keep meaning to write one up, but I quickly get lost in the weeds.

tl;dr buy a VPS, update it, install Compose + Tailscale, firewall everything off (with DO through their admin), then copy your compose.yml file and go are my rough order of operations.

@webology Thanks, this is more helpful than you might think. Just your short tldr serves as a high-level outline for a django-simple-deploy plugin.

I think I'm going to finish building the nginx-gunicorn-postgres everyting-in-one-box plugin, because it's what I know and I can pull a bunch of the general VPS utilities out into a separate module. Then it should be easier to build separate plugins focusing on different approaches.

@ehmatthes I would grab one of DO's for the n+g+p version then. It should be pretty straight forward and most of the steps seldom change from version to version.

I plan to play more with using UV to manage my python install going forward which might be too new of a concept for your plugin, but we'll be there sooner then later.

@webology I'm currently following the DO guides, but adapting them to things like using uv instead of their approach to installing Python.

@ehmatthes That's cool.

I'm slowly moving my dotfiles over to UV + Justfiles. One small change at a time.

@webology The VPS is a nice playground for using uv fully, because it's starting from a fresh state. I'm guessing it's going to make setup easier overall.

@ehmatthes It's a nice life hack. One of my main VPSes is older and can't run modern Python until I used UV to install 3.12 or 3.13 on it.

The OS is still getting updates, but they don't officially support modern Python and it's otherwise such a pain to fiddle to get it working.

I have used Docker for years to work around this, but it's nice having a system with modern Python to work with.

@webology @ehmatthes

The PONG stack 🏓
POstgres, Nginx, Gunicorn

@bmispelon @ehmatthes nice. PONicorn rolls off the tongue, too.

@ehmatthes usually Debian, Caddy, SQLite and gunicorn. Postgres if SQLite doesn’t do the trick.

Lately I started building a Docker image mostly for convenience and to not having to deal with system dependencies. But it still feels very much optional and like unnecessary overhead.

@fallenhitokiri The first version of the plugin I'm building is going to support Postgres, but it's going to have a `--db` arg where you can specify postgres or sqlite.
@ehmatthes I always use Dokku. Which uses docker containers. It works very similar to Heroku and Render. Very easy to set up and you can run multiple projects on one server if needed.

@ehmatthes I default to installing dokku these days and I will be switching to their Dockefile build.

Once I have it setup, everything just works. I have 3 installs of it now across 5 clients.

@ehmatthes Pretty much the same here, but I use Debian Stable.
@ehmatthes I’m using caprover with granian as WSGI and sqlite or postgres as DB https://caprover.com/docs/get-started.html
CapRover · Scalable, Free and Self-hosted PaaS!

Scalable, Free and Self-hosted PaaS!

@ehmatthes same here ! Add docker with traefik and crowdsec :)
@ehmatthes Debian instead of Ununtu, but yeah the rest is the same. I actually wrote a pretty big article back in 2023: https://www.loopwerk.io/articles/2023/setting-up-debian-11/
Setting up a Debian 11 server for SvelteKit and Django

I recently had to set up a brand new server for a website running on SvelteKit and its API running on Django. I am a software developer and setting up servers and hosting isn't something I normally do, so I followed a bunch of different tutorials. In this article I want to combine all these tutorials, mostly for future me, but hopefully you'll find it useful as well.

Loopwerk
@ehmatthes Dokku running on Ubuntu. CI builds an image, smoke tests it with curl, then pushes that to the host to avoid build time resource problems.
@ehmatthes
My basic deployment includes Ubuntu, PostgreSQL, uwsgi, Redis (cache, Celery broker, and Channels), Celery, Celery Beat, Postfix (MTA buffer/proxy), Daphne (when appropriate) and Nginx.
@ehmatthes I’m packaging my projects with Nix which then allows me to configure servers with https://github.com/sephii/django.nix, which takes care of the database, the web server and the app server.
GitHub - sephii/django.nix: NixOS module for Django deployments

NixOS module for Django deployments. Contribute to sephii/django.nix development by creating an account on GitHub.

GitHub
@ehmatthes docker compose, caddy, certbot, bunny.net for cdn

@ehmatthes Debian + gunicorn + ngnix + supervisord for long running scripts
For channels
Gunicorn is replaced with supervisord + Daphne

Now mostly using my own starter template with docker to host with GitHub actions

@ehmatthes My setup is Debian, caddy, postgres, gunicorn and systemd for services and timers.

@ehmatthes Debian/Percona/gunicorn+uvicorn/nginx.

But, that's because a project is old and admins prefer MySQL/Percona for some reason.

When I have a choice, I'd choose postgres.

And maybe instead of running it directly on the hardware - put it into docker/podman.

@ehmatthes Debian + SQLite + Gunicorn + Caddy + Supervisor

SQLite is remarkably capable for most projects. Maybe less so if it is write-heavy instead of read-heavy.

@ehmatthes ubuntu, postgresql, gunicorn, docker compose, caddy
@ehmatthes I handle deployment through Ansible (moved away from Docker in 2021), use Traefik for reverse proxy and SSL termination, and run Django with Gunicorn backed by either PostgreSQL or SQLite.
@ehmatthes Same, switched from uwsgi to gunicorn about a year ago.
@ehmatthes Debian instead of Ubuntu here and I probably want an option to use PostgresSQL or just SQLite.
@ehmatthes I’m like you: Ubuntu, Postgres, Nginx, Gunicorn.

@ehmatthes Docker swarm on whatever vps (loving the ARM vps from hetzner recently!); traefik and portainer for managing docker. External Managed Postgres. CloudFlare in front.
Django itself is usually granian with asgi, and a huey or django-tasks worker.

Deploy pushes container img to GitHub container registry (packages) and does an API call to the portainer stack to update to newly built image tag.
Traefik+swarm handles blue-green so no downtime, if it fails it rolls back to old container.

@ehmatthes I like this approach bc it works for fairly small apps, even with its own local Postgres it can fit on a $4/mnth hetzner system, but it also scales up to multiple nodes and provides a similar deployment strategy to a full k8s deploy which is the next place I head for bigger apps (or clients with more complex/specific demands).