Deploying Astro with Docker Compose and Traefik
/ 4 min read
In my last post, I outlined how I optimized my Astro blog’s build process to compress static files with brotli compression. This post outlines how I serve my blog from a docker container at aaronjbecker.com using docker compose and traefik, as well as how I deploy it using a bare-bones bash script with SSH agent forwarding.
This post assumes you already have a server running docker compose, as well as a Dockerfile to build and serve your Astro site. I’ll be using my own configuration for this site as an example.
step 1: configure container infrastructure
I use docker compose to build and run the container for this blog, which allows me to attach configuration labels for traefik. Traefik is an open-source tool that makes it possible to host arbitrarily many websites on a single server, with each running as a separate container. Traefik routes incoming requests to the appropriate container and automatically generates and renews SSL certificates so the sites are served over HTTPS1.
Here’s my docker-compose.yml
file, which lives in the blog’s root directory:
step 2: deploy!
First, some background on my codebase: since I’m a solo developer at the moment, I can get away with keeping all of the projects I’m working on in a single git monorepo. Being a solo developer also means that I don’t have much to gain from using a CI/CD pipeline, so I deploy directly from my git repository by manually running bash scripts when I have changes I want to deploy.
Since I don’t want to keep my GitHub SSH key on my server, I use SSH agent forwarding, which allows me to pull changes from my repository on my server using keys stored on my local machine. I use a separate SSH key to connect to my server, which provides some degree of security isolation.
Here’s the bash script I use to deploy my blog, which lives in a directory with other scripts I use to deploy other projects:
Some details like my server’s IP address and root username are redacted for obvious reasons. I have the script configured to checkout and pull whichever branch I’m working on locally so that I don’t have to create a pull request to my main branch if I’m just testing something out.
closing thoughts
Although this may mark me as crazy, I genuinely like working with docker, even if it’s overkill for this blog. My setup makes it possible to securely and efficiently host multiple websites on a single low-cost server, with each running as its own docker-compose stack.
Traefik is very good at SSL termination and routing, and docker-compose makes it easy to insert additional components to address gaps in Traefik’s capabilities, like a varnish proxy to handle caching backend responses.
As for bash scripts, they can often be illegible, but that’s less of a problem in this new age of AI-assisted coding. I like that bash scripts work on both of my development machines, a MacBook Pro and an Ubuntu desktop, without any additional setup. And I can take comfort in the fact that shell scripting isn’t going anywhere anytime soon.
Footnotes
I also run the traefik reverse proxy container with docker-compose. Configuration for that is beyond the scope of this post but here’s a tutorial from the traefik team if you’re interested. ↩