Server Setup

This is why the website was down for a while.

If you’ve been following me on twitter/fedi — or even this website — you would’ve observed that I’ve been increasingly trying to selfhost applications whenever I can. Why? Because I got a $7 $12 (upgraded to 2GB RAM because 1GB was starting to swap at times) DO droplet and I’m trying to take full advantage of it. Also, it’s a fun hobby — breaking your head over why your application isn’t showing up when the logs don’t show any failures, only to realise that you’ve mistyped the port number :p

What I host on the droplet#

Public access#

Internal access#

  • znc bouncer for IRC.
  • freshrss for RSS feeds.
  • vaultwarden for passwords (yet to migrate completely though).
  • healthcheck for checking on status of a few cron jobs I have set up on my personal laptop.

Apart from these services, I also have a few to ease in monitoring/managing all of these, again in internal access only:

All of this is behind traefik . I chose traefik because it was easiest to get working with docker containers, mainly because of its label-based configuration — I didn’t need to keep track of a separate “config” file like in nginx.

Network Overview#

I was inspired by mrkaran’s post on exposing services while self-hosting . I was already using tailscale to secure ssh access to my droplet and laptop (to connect from my phone) and also for its taildrop feature. I initially tried to copy his architecture exactly with 2 caddy instances, with one listening on the droplet’s floating IP interface and the other on the tailscale IP interface, but I decided to switch from bare-metal to containers with docker-compose. Why you ask? Because I’ve always struggled with understanding docker and this gave me a chance to understand how it works, along with being able to handle updates and backups more easily (with docker volumes).


Traefik was pretty straightforward to setup. The config from the basic example was enough to get me started. I mapped ports 80/443 of the container to those of the tailscale IP’s interface and mapped 81/444 to the floating IP’s interface’s 80/443 ports. Since I like having each service on a subdomain (instead of a subdirectory under the apex domain), I create A records pointing to the droplet’s tailscale IP for DNS. This isn’t an insecurity since the IP range falls in the CGNAT space and hence is treated as a private IP address.

One downside with traefik compared to caddy was HTTPS — caddy required no configuration out of the box for automatic HTTPS. However, setting up automatic https for all hosts with traefik is a breeze compared to nginx (which is what I was using previously). I use cloudflare for my nameservers2 and manage everything on it and hence I configured cloudflare as the provider as described here .

    - "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare"
    - "[email protected]"
    - ""
    - CLOUDFLARE_EMAIL=xxxxxxxxxxx
    - CLOUDFLARE_DNS_API_TOKEN=xxxxxxxxxxxxxxxxx

I also wanted to route all HTTP traffic to HTTPS and the following established that.

    - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
    - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
    - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
    - "traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true"

Adding a new container to the network is super easy — just a few labels for the container in docker-compose.yml, and adding it to the traefik-network.

    - traefik-network
    - "traefik.enable=true"
    - "traefik.http.routers.dashboard.rule=Host(`<subdomain>`)"
    - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
    - "<service name>.loadbalancer.server.port=<port>"
    - "traefik.http.routers.dashboard.entrypoints=<private/public>"

  1. do-agent is great, but not very comprehensive. ↩︎

  2. trying to transfer my domain to cloudflare, but neither of the payment methods work for me :( ↩︎

© Akilesh Kannan 2024 · Made with Neovim and Hugo · Licensed under CC BY-NC-SA 4.0