···17171818### Why?
19192020-* NGINX designed for single server topology
2121- * No service discovery
2222- * Middleware is hard
2323-* Relying on SWAG doesn't feel first class
2424- * ENVs for cert generation are unwieldy
2525- * Still kind of a hack, wish it was 1st party
2626- * crowdsec mod encourages tight coupling with SWAG scripts
2727- * cloudflare tunnels encourages tight coupling with SWAG scripts
2828- * service discovery mod designed for single host
2929- * my fork works but feelsbadman.jpg
3030-* No birdseye
3131- * No dashboard or metrics
3232- * Diagnosing config errors is hard
3333- * Should not need https://github.com/dvershinin/gixy to do this
2020+##### NGINX Configuration is Static
2121+2222+This is the single most important reason I had for migrating away from NGINX.
2323+2424+In a homelab environment where services are being spun up/down, created, moved between hosts, renamed...having to 1) remember to update NGINX config and then restart it 2) keep track of IP:PORT or specific config per application is a pain. NGINX was designed during a time when network topology wasn't so dynamic, it's not built with a homelab use-case in mind. It's also not built with today's paradigms in mind such as [first-class environmental substitution](https://www.baeldung.com/linux/nginx-config-environment-variables) or [easy-to-read config validation.](https://github.com/dvershinin/gixy)
2525+2626+##### NGINX Service Configuration Ownership
2727+2828+As my homelab continues to grow I have gravitated towards each Stack owning its own configuration. Through mechanisms like `environment` in `compose.yaml` or configs committed to git alongside `compose.yaml` etc.. having the service-as-code live next to all the data needed run the service makes it more portable and reduces the cognitive scope required to configure it. NGINX config files need to be physically accessible to it and that is not a *feasible* option when services run on other hosts.
2929+3030+##### SWAG is Tightly Coupled and Opinionated
3131+3232+LSIO does an excellent job making setup with NGINX easy by using [SWAG](https://docs.linuxserver.io/general/swag/). For simple setups and users just dipping their toes into the space it's a fantastic tool for getting started quickly without requiring any hand holding.
3333+3434+However, it has shortfalls which appear for more complex use-cases. Some of these are limitations of nginx such as needing the user to [edit .ini files for DNS ACME challenge while other solutions only need ENVs](http://localhost:4000/posts/migrating-to-traefik/#wildcards).
3535+3636+Others are due to the reality of limited developer-hours needing to fulfill only the most common use-case, like LSIO's [cloudflare docker mod](https://github.com/linuxserver/docker-mods/tree/universal-cloudflared) configuration only working with one domain even though a tunnel can be used for multiple domains -- one domain is the most common use-case and easiest to script for. In this scenario "fixing" the problem means refactoring the entire stack to remove universal-cloudflare and implementing your own `cloudflared` container.
3737+3838+If most scenarios end with the user having to implement the decoupled solution anyways...why not consider other reverse proxy solutions since we aren't tied to SWAG anymore?
3939+4040+##### Lack of Dashboard
4141+4242+NGINX does not have a dashboard unless you are paying for enterpise (NGINX Plus). SWAG has a [docker mod for Goaccess](https://github.com/linuxserver/docker-mods/tree/swag-dashboard) but that is more traffic-focused then NGINX config-related. [NPM does have a dashboard](https://nginxproxymanager.com/screenshots/) as does [Traefik](https://doc.traefik.io/traefik/operations/dashboard/).
4343+4444+Having a dashboard with relevant config metrics and error troubleshooting becomes a must as the number of services served grows.
4545+4646+##### NGINX/SWAG does not have first-class Service Discovery
4747+4848+SWAG offers the docker mod [swag-auto-proxy](https://github.com/linuxserver/docker-mods/tree/swag-auto-proxy) which generates nginx confs for services discovered by docker label on the same machine NGINX is running on. I forked this as [swag-auto-proxy-multi](https://github.com/FoxxMD/docker-mods/tree/swag-auto-proxy-multi) and wrote new functionality to enable it to work with multiple hosts using [docker-socket-proxy](https://docs.linuxserver.io/images/docker-socket-proxy/).
4949+5050+While this does work it's not *good.* The inner workings are a mess of bash scripts that generate nginx confs and reload *the entire application* every time new changes are made. It's also barebones compared to applications like Traefik that have [service discovery as a first-class feature.](https://traefik.io/glossary/service-discovery/)
34513552### Requirements/Spec
36535454+Before YOLO'ing another reverse-proxy solution I came up with a list of requirements that needed to met. I have 60+ stacks, over 100 containers, running on 7 machines.
5555+5656+If I am going to switch and do all the work to get most of these served then the new solution was going to have to 1) have **feature parity** with SWAG + features I use with it (auth) and 2) be **easier** to implement with all my machines than the current swag + auto-proxy setup.
5757+5858+The requirements:
5959+3760* Must be able to host services with web routing parity WRT NGINX configs
3861* Cert management must be easier than SWAG
3962 * Must be able to validate via dns challenge
···47704871### Evaluating Other Solutions
49727373+* [NGINX Proxy Manager (NPM)](https://nginxproxymanager.com)
7474+ * Considered since almost all my confs would be able to stay the same
7575+ * Eventually decided against due to lack of first-class service discovery
5076* [Caddy](https://caddyserver.com/)
5151- * Requires third party module just for docker discovery https://github.com/lucaslorentz/caddy-docker-proxy
7777+ * Requires third party module for [service discovery](https://github.com/lucaslorentz/caddy-docker-proxy)
5278 * [Plugins require custom builds](https://github.com/serfriz/caddy-custom-builds) including cloudflare/crowdsec
5379* [GoDoxy](https://github.com/yusing/godoxy)
5454- * Promising but too new
8080+ * Has dashboard, dynamic reloading, automatic certs
8181+ * Has first-class docker service discovery via labels and supports [multiple hosts](https://github.com/yusing/godoxy/wiki/Configurations#setting-up-providers)
8282+ * Strongly considered but too new...lack of docs and user base. My homelab is too large and in "production use" to be a beta tester
55835684## Satifying Requirements with Traefik
57855858-Overviews of how each [Requirement](#requirementsspec) was met
8686+I eventually settled on [Traefik](https://traefik.io/) after due-diligence gave me enough confidence to think I could satisfy all of the [Requirement](#requirementsspec).
8787+8888+The actual implementation is always more difficult than how it looks on paper but in the end it met every requirement and ended up being easy to maintain and work with!
8989+9090+In each section below I lightly cover the differences between SWAG/Traefik, what was needed to migrate to Traefik, and how I implemented it along with examples, if necessary.
59916092### Web Routing Parity
6193···1238127012391271In addition to network-level separation this requires *more* explicit configuration in the compose stack which lessens the chance of a sloppy copy-paste even further.
1240127212411241-#### How to do
12731273+#### Setup {#separate-network-setup}
1242127412431275First, create [externally-managed docker networks](https://docs.docker.com/reference/cli/docker/network/create/) for your two, new web ingress networks. Make sure you [specify an unused subnet](https://docs.docker.com/reference/cli/docker/network/create/#specify-advanced-options) for the external network (we can use it in the next post on firewalling external networks...)
12441276