💾 Archived View for wilw.capsule.town › log › 2022-04-09-nginx-traefik-selfhosted.gmi captured on 2023-07-10 at 13:42:03. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-04-19)

-=-=-=-=-=-=-

🏡 Home

Back to gemlog

Self-hosting apps and services using Traefik reverse proxy

Posted on 09 April 2022

I often talk about self-hosting on this blog, and I'm certainly a big fan of being able to control my own data and systems wherever possible (and feasible). I've recently switched from using Nginx to Traefik as a reverse proxy for my server and for terminating TLS connections.

In this post I'll talk a little about why and how I made this change.

My (until recent) setup

I self-host a number of services; including Nextcloud [1] for file storage and sync, Gitea [2] for git syncing, FreshRSS [3] for RSS feed aggregation and management, Monica [4] for relationship organisation, and a few other things too.

1

2

3

4

To date, I've mostly relied on Nginx [5] for providing a reverse proxy, enabling me to run many of these services on a single VPS. I run everything, including Nginx, using Docker containers, which I find much more convenient and easy to manage.

5

Individual services are managed using Docker Compose [6], which allows me to keep services that rely on multiple containers (such as those that depend on both a web server and a backend database server) logically separated and organised. I use a single `nginx.conf` file, used by my Nginx container, to manage these services as virtual hosts and for configuring TLS certificates.

6

Why I wanted a change

This setup works well and has served me well for several years. However, it isn't without its drawbacks;

Most of these pain points are related to the reverse proxy setup itself, rather than the individual services (which can happily run indefinitely). To me, it felt like the webserver side of things should be the most "boring" and least time-consuming, when in reality it is the opposite.

I wanted to find a new solution that would help me solve these issues and allow me to focus more on maintaining and working on the services themselves.

 Traefik as a reverse proxy

Traefik [7] provides a proxy system [8] that I've used before in Kubernetes setups. It acts as a reverse proxy, TLS terminator, load balancer, and much more. It's designed to work well with microservices running in containers, and whilst Nginx can certainly achieve all of this too, the nice thing about Traefik is that it does this for you **dynamically and automatically**.

7

8

It can easily be configured using Docker Compose, and all the TLS provisioning is handled automatically via Docker _labels_. The documentation [9] covers the setup well and in detail, but below I will run through (roughly) how I made the switch from Nginx to Traefik.

9

 Step 1: Firstly, stop all of your running services

If you use Compose, you can simply run `docker-compose down` to take your services down. At the very least, make sure you are shutting down your Nginx container.

_Note: If your user is not in the `docker` group you may need to use `sudo` to run `docker-compose` commands._

Step 2: Next, write a new Docker Compose file

Create a new directory to house your Traefik setup, and in here write a new `docker-compose.yml` file, as shown below. In this example I am using Traefik to route requests through to Nextcloud and FreshRSS instances:

version: '3'

services:

  # The Traefik service
  reverse-proxy:
    image: traefik:v2.6
    command:
      # Tell Traefik that we're working in a Docker environment
      # This allows it to dynamically pull out the configuration
      - "--providers.docker"
      # Standard ports for HTTP/S
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      # ACME TLS certificate resolver setup. Remember to change your email address here
      - "--certificatesresolvers.myresolver.acme.email=me@example.com"
      - "--certificatesresolvers.myresolver.acme.storage=/etc/traefik/acme/acme.json"
      - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
    ports:
      # Map our web traffic ports to the host network
      - "80:80"
      - "443:443"
    volumes:
      # Mounting this volume is important to ensure certificates can be persisted
      - ./acme:/etc/traefik/acme
      # This volume is to enable Traefik to communicate with Docker
      - /var/run/docker.sock:/var/run/docker.sock

  # The FreshRSS service
  # (see https://github.com/FreshRSS/FreshRSS/tree/edge/Docker for more info)
  freshrss:
    image: freshrss/freshrss
    environment:
      - "CRON_MIN=3,33"
      - "TZ=Europe/London"
    volumes:
      - ./freshrss_data:/var/www/FreshRSS/data
      - ./freshrss_extensions:/var/www/FreshRSS/extensions
    labels:
      # Here we add a label to tell Traefik how to route to this service by domain
      # Remember to change this host value.
      - traefik.http.routers.freshrss.rule=Host(`rss.example.com`)
      # These two labels tell Traefik to setup TLS for this service
      - traefik.http.routers.freshrss.tls=true
      - traefik.http.routers.freshrss.tls.certresolver=myresolver
    restart: always

  # The Nextcloud service
  # (see https://hub.docker.com/_/nextcloud for more info)
  nextclouddb:
    image: mariadb:10.5
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    restart: always
    volumes:
      - ./nextcloud-data/db:/var/lib/mysql
    environment:
      # Remember to change these values:
      - MYSQL_ROOT_PASSWORD=<PASSWORD>
      - MYSQL_PASSWORD=<PASSWORD>
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
  nextcloud:
    image: nextcloud:22
    volumes:
      - ./nextcloud-data/storage:/var/www/html
    restart: always
    labels:
      # Here we add a label to tell Traefik how to route to this service by domain
      # Remember to change this host value.
      - traefik.http.routers.nextcloud.rule=Host(`nextcloud.example.com`)
      # These two labels tell Traefik to setup TLS for this service
      - traefik.http.routers.nextcloud.tls=true
      - traefik.http.routers.nextcloud.tls.certresolver=myresolver

The above may seem quite verbose, but this is literally _all_ you need. After running `docker-compose up -d` (and you've changed your domains and passwords, etc.) your services will be up and running with TLS certificates automatically provisioned.

I've added comments to the file to explain some of the concepts further, but below I'll add a few extra notes:

Whilst the above setup achieves what we need, it is simple. There is much more you can do, such as route using specific paths and manage load balancing. For more information, please see the documentation [10].

10

More complex services

As mentioned earlier, I also run Gitea as a service on my VPS. Gitea serves a GitHub-style web UI in addition to a git endpoint. Since I interact with the git endpoints over SSH, this service ends up relying on two ingress points: one for HTTP web traffic and another for SSH git traffic.

When I first set this up, Traefik was routing web traffic through to Gitea's SSH port, which obviously caused problems. As such, I needed to add extra configuration to tell Traefik which port to use, as described below:

...

  gitea:
    image: gitea/gitea:latest
    restart: always
    environment:
      - USER_UID=1000
      - USER_GID=1000
    volumes:
      - ./gitea_data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    expose:
      - "3000"
    ports:
      # Ensure we map port 22 to the host for incoming SSH git traffic
      - "22:22"
    labels:
      # The below three labels are as described earlier
      - traefik.http.routers.gitea.rule=Host(`git.example.com`)
      - traefik.http.routers.gitea.tls=true
      - traefik.http.routers.gitea.tls.certresolver=myresolver
      # Add a fourth label to tell Traefik where to route web traffic to
      - traefik.http.services.gitea.loadbalancer.server.port=3000

After running `docker-compose up -d` again, your Gitea instance will be running, along with its web UI and git endpoint.

_Note: if you use port 22 for standard SSH to your host server, you can map a different port for your SSH git traffic. For example, `2200:22`._

Conclusion

Having made this switch, I find things much more logically organised and robust. The reverse proxy is just as performant (at least, for my use) as Nginx and I get extra peace of mind in that I can trust Traefik to handle web traffic, routing, and certificate renewals without any manual intervention.

Reply via email

Back to gemlog