My personal reasons to not run my Nginx reverse-proxy inside Docker
Beware that this article is controversial to many people's opinions. It is no Docker-bashing prose!
The following text is based on my situation why I consider wrapping my reverse-proxy (Nginx) with Docker as a disadvantage. Your situation is likely different.
I believe that Docker is a useful technology that serves us software engineers well in many areas. I use it at work all the time although I do not consider myself a Docker pro.
As every technology it has its merits and demerits in certain situations. I go over these carefully weighing the pros and cons of it and explain my judgement.
My setup
Explaining how my infrastructure looks like before I go into my analysis will help to set a baseline for you to understand and compare it to your situation.
I use Nginx as a reverse-proxy that serves around 10 websites and web applications. They mostly live on one VPS that runs Ubuntu Linux.
I change my Nginx configuration approximately once in a few months. Most of the time such changes are adding a new website by configuring a new virtual host.
Problems that Docker solves
A short refresher on what Docker does for us developers.
- Ensures compatibility of the service (process) with the underlying OS
- Ensures compatibility of the service with the libraries and dependencies (avoids dependency hell)
- Prevents configuration drift (infrastructure is written as code)
- Eases spinning up more instances (e.g. for testing or scaling)
- Infrastructure-as-code serves as documentation
My assessment process
In order to assess situations like this in a qualitative way I take inspiration from the "Failure mode and effects analysis".
The FMEA was developed in the aerospace and military industry in the 50s to assess the reliability of (technical) solutions before building them.
Three probability metrics are important:
- Severity
- Occurrence
- Detection
It's sounds a bit abstract but the concept is pretty easy.
The FMEA gives you a framework to answer the question:
How much should I care about a certain problem therefore bother solving it?
The trivial case as an example:
If something is unlikely to occur, easily detectable by the user (then also fixed) and has low severity then do not bother with the problem nor their solutions!
All other cases are non-trivial. Depending on who does the analysis of these metrics the outcome varies because everybody has a different risk perception.
Docker is one dependency more
You might object this section as "I use Docker already so no additional dependency disadvantage at all".
It is actually about Nginx depending on Docker. When Docker is down then I cannot even show an error page. Everything that something depends on makes it a little more likely to introduce failures.
In general, I try to have as little dependencies as possible because of the increased failure modes.
I happily put Nginx into Docker if the problems (that Docker solves) are somewhat likely to occur in practice regarding running Nginx as a reverse proxy. If not then I qualify Docker as an unnecessary dependency since it solves a hypothetical problem in my particular situation.
Nginx has few, stable dependencies
The external dependencies that Nginx relies on are:
libc
,zlib
,libssl
,libcrypt
,libpcre
andiproute2
.
Apart from iproute2
all of these libraries themselves only depend on libc
and have proven their maturity over decades.
My server contains no customized versions of these libraries. I have not encountered any compatibility issues, ever. Therefore the safety net of dependency isolation and fixation which Docker definitely solves is likely never needed.
I will re-evaluate the "I do not need dependency isolation for Nginx" decision when I suffer from a dependency issue with Nginx.
Docker makes it harder to have Nginx "always on"
Nginx designed for the use case to never shut down. I never shut it down except for OS kernel security patches.
You can upgrade the binary in-place under load. Nginx gracefully finishes serving existing connections and new worker processes start with the new binary. It does not tear down any connections that are in progress.
If you change the configuration you just issue nginx -s reload
and Nginx picks it up by the same graceful hot-reload mechanism as when upgrading the binary. Marvellous!
Docker just gets in the way here. Hot-reloading and in-place upgrades of Nginx go against the containerization philosophy. Containers are about immutability. Instead of updating existing instances you tear down the old instance and spawn a new one.
I could live with a few seconds of service interruption for going the immutable-container way of spawning new instances.
But it just feels not being worth the investment. I'm confident that at some point I would screw up my own Docker setup and my reverse proxy is down because of it.
I don't build 5-nines reliable systems but when Nginx is down then I cannot even show an error page which is suboptimal. My paying customers should see at least an error page with contact information.
Configuration of the reverse proxy in production is unique
The virtual host configuration entries are associated with their respective domain names. DNS entries point to the server which runs my reverse proxy. If we run another instance of Nginx with the same configuration as in production - let's say for development - we need a full-blown DNS server to make it work.
The reverse proxy is a "singleton service". It is the entry point to everything.
Having more than one entry point to something means that another instance - with a single entry point! - needs to decide where to enter your system.
Hence you probably just have only one reverse proxy unless you do round-robin DNS load balancing or some Google-scale hierarchical load balancer setups.
That said I've never had the need to spin up another instance. If I need to I have a bash script and rsync
the configuration file from my project directory to the server.
Summary
For my setup Docker doesn't solve a problem I've faced. According to the KISS principle and my qualitative FMEA analysis I would bother with a solution to non-existent problems which in itself is a problem.
I'm willing to revisit and potentially change my opinion if my situation changes or I gain new insights which make the aforementioned points inappropriate.