Nginx to Caddy to Traefik
I've hosted half-baked experiments on and off for over a year or so now over at digital ocean. You know how life can just, get in the way of programming. It's been exceedingly frustrating getting 30-45m spurts of time to work on something over the span of a week or more, wherein most of that time is spent only 'catching up' to what I have done before. I yearned for a toolset that would just allow me to get going with blogging, hosting a minecraft server, or getting a project up and running. All three of those things have fairly simple answers in my mind now, that I can get up and running in an arbitrary environment almost trivially, pretty much entirely because of docker hub and all the great images people create.
At least for web-hosting and blogging, I think I've found the magic sauce - but to tell you the punch line would be selling you short - the journey is really where the learning took place.
So, firstly, I wanted to blog. How does one blog in 2017/2018 era of tech? Well, medium is quite popular, but people have their gripes with the medium platform. I could just post on reddit or hackernews, but I knew I wanted to self-host. Especially since I had underutilized bandwidth on a digital ocean droplet, and a domain name I'm paying for and basically not using.
Now, how does one self-host? Do you just roll your own infrastructure on top of a VPS? Use a static-site generator like hugo
? Actually with hugo, it would be easier to host on github, gitlab, or over at netlify like where my wife is initially hosting her portfolio.
I wanted to manage my content more than what hugo and other tools offered. So I knew I wanted a ghost
blog, since it was open source, completely theme-driven, and just real nice. They have a solid dev team, and a good paid cloud solution to fund the ongoing development of the product.
But my journey hit a rocky road... You see, I wanted to write my own NodeJS app at thenewells.us
, and then have another node app be the ghost blog at subdomains. Seems like a good idea, initially, but then the root NodeJS app became a bottleneck. I have the code hosted over at GitLab for that root project, but the direction, vision, and time needed wasn't something I could give at the moment. So the whole thing stalled.
A major part of the time commitment was my naivete in trying to setup a secure NodeJS web app, rather than relying on a proxy like nginx, and when I figured out that wasn't what people did, I was still naive in trying to get my own nginx configuration up and running without learning about nginx configuration.
Eventually, I started playing with docker
and docker compose
more, and that helped alleviate the necessary environment configuration to get everything working, but I was still writing my own nginx config (and using it by either the Dockerfile COPY
, or mounting the config as a volume into the container).
Then I saw the light.
Photo by Jake Givens / Unsplash
I was looking at kubernetes, docker swarm, and Caddy. Caddy is a breath of fresh air if you have ever needed to configure apche or nginx. It handle https automatically via Lets Encrypt, and does the 'right thing'™ as you would expect.
But my voyage into configuration and web server tech didn't stop there. I knew I wanted a Ghost blog, and I had setup a local ghost blog via docker, and I knew I wanted to set up an identical, but separate blog for my wife - and have it all orchestrated with Docker.
Enter traefik.
It supports k8, docker, and many other backends - perfect for my use-case of orchestrating docker containers on a single host (for now), while having an easy path to scale to other machines later. I got a scalable, https configuration in 58 lines. With just two files - seriously, here are the original files used in conjunction with the traefik
image that handles incoming traffic to thenewells.us
:
docker-compose.yml
version: "3"
services:
traefik:
image: traefik:1.3.6-alpine
command: --docker
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./traefik.toml:/traefik.toml
- ./acme.json:/acme.json
labels:
- "traefik.frontend.rule=Host:admin.thenewells.us"
- "traefik.port=8080"
networks:
- web
networks:
web:
external: true
traefik.toml
debug = false
checkNewVersion = true
logLevel = "ERROR"
defaultEntryPoints = ["https","http"]
[web]
address = ":8080"
[web.auth.basic]
users = ["admin:encryptedadminpass"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[retry]
[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "thenewells.us"
watch = true
exposedbydefault = false
[acme]
email = "newell.sean.a@gmail.com"
storage = "acme.json"
entryPoint = "https"
onHostRule = true
onDemands = false
Now, to host the blogs, it's just another simple docker-compose file since traefik listens for events on the docker socket. For the two ghost blogs (for my wife and I) there's only one compose file.
docker-compose.yml
version: "3"
services:
sean-blog:
image: ghost:1-alpine
container_name: sean-blog
restart: always
networks:
- web
- internal
expose:
- "2368"
volumes:
- snew_ghost_data:/var/lib/ghost/content
labels:
- traefik.backend=seanblog
- traefik.docker.network=web
- traefik.frontend.rule=Host:sean.thenewells.us
- traefik.port=2368
nicki-blog:
image: ghost:1-alpine
container_name: nicki-blog
restart: always
networks:
- web
- internal
expose:
- "2368"
volumes:
- nicki_ghost_data:/var/lib/ghost/content
labels:
- traefik.backend=nickiblog
- traefik.docker.network=web
- traefik.frontend.rule=Host:nicki.thenewells.us
- traefik.port=2368
networks:
web:
external: true
internal:
external: false
volumes:
snew_ghost_data:
nicki_ghost_data:
It's taken way too long, but now that I have this all up and running, I hope to stick with traefik
and docker
for a while, perhaps dipping more into k8, so I can get more proficient with it.
Happy Coding!