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 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.
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
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
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 = "email@example.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.
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
docker for a while, perhaps dipping more into k8, so I can get more proficient with it.