Compare commits
3 Commits
21134f682a
...
e50dc994cb
Author | SHA1 | Date | |
---|---|---|---|
e50dc994cb | |||
812adf9c0d | |||
b94c1dac5e |
3
.nova/Configuration.json
Normal file
3
.nova/Configuration.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"workspace.name" : "Personal Website"
|
||||
}
|
178
src/pages/blog/2022-04-09-nginx-traefik-selfhosted.md
Normal file
178
src/pages/blog/2022-04-09-nginx-traefik-selfhosted.md
Normal file
@ -0,0 +1,178 @@
|
||||
---
|
||||
date: "2022-04-09T13:22:00Z"
|
||||
title: "Self-hosting apps and services using Traefik reverse proxy"
|
||||
description: "Why and how I replaced my Nginx setup with Traefik."
|
||||
tags: [technology, selfhost]
|
||||
---
|
||||
|
||||
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](https://www.nextcloud.com) for file storage and sync, [Gitea](https://gitea.io) for git syncing, [FreshRSS](https://www.freshrss.org) for RSS feed aggregation and management, [Monica](https://github.com/monicahq/monica) for relationship organisation, and a few other things too.
|
||||
|
||||
To date, I've mostly relied on [Nginx](https://www.nginx.com) 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.
|
||||
|
||||
Individual services are managed using [Docker Compose](https://docs.docker.com/compose), 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.
|
||||
|
||||
## Why I wanted a change
|
||||
|
||||
This setup works well and has served me well for several years. However, it isn't without its drawbacks;
|
||||
|
||||
* The basic system relies on manual and semi-tedious config entries to my `nginx.conf`, including HTTP -> HTTPS redirects, enumerating virtual hosts, and other deeper Nginx stuff I don't always fully understand.
|
||||
* TLS certificate provisioning and renewal: I either need to do this manually or remember to setup (and then trust) a cron job to renew things properly.
|
||||
* Domain verification for Let's Encrypt: configuring webroots and interception of ACME requests for each virtual host.
|
||||
* Needing to restart (or automate the restart) of the webserver after receiving new certificates.
|
||||
|
||||
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](https://traefik.io) provides a [proxy system](https://traefik.io/traefik) 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**.
|
||||
|
||||
![Traefik logo](/media/blog/traefik.png)
|
||||
|
||||
It can easily be configured using Docker Compose, and all the TLS provisioning is handled automatically via Docker _labels_. The [documentation](https://doc.traefik.io/traefik) covers the setup well and in detail, but below I will run through (roughly) how I made the switch from Nginx to Traefik.
|
||||
|
||||
### 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:
|
||||
|
||||
```docker
|
||||
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:
|
||||
|
||||
* I've setup four services: the Traefik reverse proxy itself, a `freshrss` container, and two containers for Nextcloud: `nextcloud` (the software itself), and `nextclouddb` (the MariaDB database for Nextcloud).
|
||||
* Since `nextclouddb` doesn't need to (and shouldn't, for security) be exposed to the internet, we do not add any Traefik routing labels to this service.
|
||||
* Adding a mount for the `acme.json` storage is important in your Traefik setup to avoid hitting Let's Encrypt rate limits.
|
||||
* I added volumes for FreshRSS and Nextcloud so that data can be persisted.
|
||||
* Notice how all Traefik routing and TLS configuration on services is simply handled with labels.
|
||||
|
||||
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](https://doc.traefik.io/traefik/routing/overview).
|
||||
|
||||
## 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:
|
||||
|
||||
```docker
|
||||
...
|
||||
|
||||
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.
|
BIN
static/media/blog/traefik.png
Normal file
BIN
static/media/blog/traefik.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 149 KiB |
Loading…
Reference in New Issue
Block a user