Docker, Django, Traefik, and IntercoolerJS is My Go-To Stack for Building a SaaS in 2021
In case you are wondering, that SaaS is Curabase:
I recently published some thoughts on Django being a great framework for applications. This post expands on that to include the other pieces of infrastructure from development to production environments.
I have used this stack (or ones that look a lot like it) to build small SaaS apps in 2018, 2019, 2020, and now in 2021.
My entire stack
- Linux Server/VM Hosted anywhere (I like Azure, Digital Ocean, or Scaleway) (edit: 2021 – I'm in the process of moving everything over to a dedicated server at Hetzner.)
- Docker. Just plain docker
- Traefik for reverse proxy and TLS with LetsEncrypt
- Postgresql running in docker
- Django in a container
- Intercoolerjs for easy and slick Ajax-like front-end work (Edit: 2021 - the creator of intercooler has released HTMX, the successor to IntercoolerJS.)
- Sentry for catching production bugs (easy three-lines added to your config)
- Bitbucket pipelines for CI/CD (Edit: 2021 - I no longer bother with CI / CD for personal projects. It it too much tooling for no benefit)
- ZeroTier for VPN/ControlPlane
For smaller projects I run tests locally in docker containers and then push directly into production. I don't bother with full CI/CD because I don't need the complexity of it all. That said, I do like Bitbucket Pipelines.
Woah! that is a lot to unpack here. Let's visualize this another way.
- Virtual Machine
- Docker
- Django
- Volume mounted data disks
- Workers (Long-running django commands)
- Volume mounted data disks
- Postgres
- Volume-mounted data disks
- Traefik
- Django
- Zero-Tier
- SSH
- Docker
Was this helpful? Let me know...
Hosting
Your stuff needs a home (yes, even in the "serverless" world. LOL). My personal preferences are Azure, Digital Ocean (affiliate link), or Scaleway. They each offer enough compute, networking options, storage, and basic services to build out proofs-of-concept or whatever you might need.
Another honorable mention here is Hetzner. They offer a good level of hardware, service and price.
The Virtual Machines
For those side projects and many enterprise applications, scale is not an issue. That means that I will not serve thousands of simultaneous users or handling terabytes of data. Therefore I can get by on the smaller offerings – usually under $20/month. Even Azure (the most expensive of the three) offer their burstable VMs. Generally I like to go with Scaleway's Developer line of servers.
Notice that Kubernetes is missing from my stack? When scale is not an issue then you don't need Kubernetes.
Docker. Just plain Docker.
I do not rely on the OS vendor (Ubuntu) to make sure that I run the latest Docker on new VMs. Therefore I use the nice little curl|bash
technique.
curl -s https://get.docker.com | sudo bash
This cure one-liner will get the best and most recent version for your machine running.
Traefik for Reverse Proxy
Traefik has been a God-send since I found it. Nginx is great, but it was not built for the Docker universe. Traefik has two killer features that have saved me hours upon hours:
- Automatic TLS with LetsEncrypt. Literally set-it-and-forget-it. With the right API keys and DNS provider you can also do verification with DNS.
- Automatic no-reload configuration using docker labels. When you spin up new services Traefik will pickup the changes automatically because it listens to all Docker-related events. This makes it incredibly convenient to add, remove, or merge services as needed without any hassle.
My only comment on Traefik is that there is a bit of a learning curve. You have to decide how you want to configure it (config file, command line options, yaml, or docker labels, or use a combination!)
Another note here: I have already published my production configuations for Traefik here.
Postgres for Database
Tried and True, PostgreSQL has never let me down. I usually attach one of these containers to a project that needs it without any difficulty. I simply spin up the container, bind the ports, and then bind the data volume to my host disk. Done and Done.
docker-compose.yml
version: '3.1'
services:
db:
container_name: postgres
hostname: postgres
image: postgres:11
restart: always
environment:
POSTGRES_PASSWORD: secretsonly
volumes:
- ./data:/var/lib/postgresql/data
ports:
- 5432:5432
networks:
- web
networks:
web:
external: true
Dockerized Django for Web
Docker deploys nicely in a container, and I have been doing it for a few years now. The benefits of matching your development environment to your production one cannot be overstated, and I have Docker to thank for that.
Edit: 2021, I've got a reference Django project up that I use as my template for new projects: https://github.com/simplecto/django-reference-implementation
Django commands for Asynchronous tasks
Also, for asynchronous tasks I simply use custom Django commands which are a part of the standard framework. The pattern here is a simple while
loop with a sleep()
period. It polls the database for relevant actions and then does it's thing.
Edit: 2021 - This has paid off in a big way. I have been running a website screenshot project for over a year now with this pattern. It takes about 1500 website screenshots per day. All of it is scheduled and managed by a Django command.
That project lives here: https://github.com/simplecto/screenshots
IntercoolerJS, because who needs the complexity?
I have a lot to say about this, but that will have to go into a series of posts. The tldr; here that I use this lovely little javascript library along with jQuery (yeah, it is 2020, and I still use jQuery) to make parts of my applications feel like single-page-apps but not really.
IntercoolerJS keeps that old-school "Ajax" (remember that word) goodness and allows me to update the DOM with HTML from the backend. It is seamless, smooth, and really convenient for things like logins and small form updates.
I strongly suggest you check it out: Learn more about IntercoolerJS
Edit 2021: The creator has released HTMX, the successor to IntercoolerJS.
Sentry to catch production errors
I make mistakes. Lots of mistakes--but there is no need to show them to my users, right? Sentry gives me an easy and convenient way to capture production bugs as they happen. Some cool things about them:
- Open source-ish (relicensed here), so you can host yourself if that is your thing.
- Easy few lines added to your
settings.py
file in Django and that is it. - Tight integration to your Git repos and Issue tracking systems for full production defect traceability.
Another nice thing is that you can disable it for development.
Bitbucket Pipelines for CI/CD
Edit: 2021 - I no longer use CI/CD for deployment of my one-man projects. It is too much tooling and complexity. I simply run tests with PyCharm and ship directly to production from my development machine.
There are so many CI/CD offerings out there today, but I have been happy with Bitbucket's offering called Pipelines. They offer you a few hundred minutes free every month with the option to top-up as needed for a small fee. I have rarely had problems, and I really enjoy their YAML configuraiton / directive files.
Using the bitbucket-pipelines.yml
file I have been able to do full end-to-end testing by spinning up multiple docker containers, loading the databases, and running hundreds of test in just a few minutes. This was key in speeding things up in our team and enabling 5+ pushes to production per day.
ZeroTier for VPN/Control Plane
Finally, we come to the bit of tech that is largely optional but nice to have. Zerotier is a unique kind of network/VPN that I use to link all my personal machines. It works though firewalls (at home, in office) and offers an easy 1 minute setup.
Using ZeroTier in my last company we were able to remove the SSH Jump Servers which caused a headache in terms of key management and shared bandwidth on a single machine.
Zerotier work on Linux, Mac, Windows, Android, and iPhone, so you are pretty much covered.
The one downside to ZeroTier is that I don't entirely understand how it works. It is a lot like MacOS or iPhone in that it "just works" as expected and I rarely (never) have problems. That is a strength from a user experience perspective but a WTF? from a CTO perspective.
Conclusion
Hopefully this deeper-dive will prompt some interest and curiosity about Docker, Django, Traefik, and especially IntercoolerJS. It is simple, easy to work with, and allows you to grow out of it when the time comes.
Edit 2021 - As you can see not a lot has changed. I've been able to add more code to my public repositories that flesh out the ideas expressed here over a year ago.