Nginx reverse proxy + Docker mount volume

Today I did two things for my blog project: added a proxy on my Nginx server for the api connection and mounted /data/db directory from the host to the docker container to achieve data persistency.

First, Nginx proxy.

The idea/goal isn’t that complicated. There are three docker containers running on my production machine for the blog, by service names:

  1. blog-api, the api server that listens to :1717
  2. web, the frontend nginx server that listens to :80
  3. mongodb, the mongodb database that listens to :27017

Before today, I had to configure the port in the frontend code, so that the frontend calls the api endpoints with the base url and the port number. If this didn’t bother me enough, for the image uploading, all of the urls for embedding the images in the posts have the port numbers in them, so they look like this:

It is against intuition for the port number to be shown to users, so I began looking for a solution. Nginx turns out to have this reverse proxy configuration that allows you to proxy requests to some location to some other port number, or even a remote server. It’s called “reverse” proxy because unlike “normal” proxies, the nginx server is the “first server” that the client connects to, whereas in other proxies the “proxy server” is the first and the nginx server would be behind the proxy.

With some trial and error, I came to this:

http {
	upstream docker-api {
		server blog-api:1717;

	server {
		listen 80;
		root /app/dist;
		location / {
			try_files $uri /index.html;

		location /api {
  			rewrite /api/(.*) /$1  break;
			proxy_pass http://docker-api;
			proxy_set_header X-Forwarded-Host $server_name;
			proxy_set_header X-Real-IP $remote_addr;
	# other configs

The upstream server name following the upstream keyword can be arbitrary, but the server name blog-api matches with my docker service name and will serve as the host name for the second hop as shown in the proxy_pass field below.

The location /api block does the proxying. The first line rewrites the request so that the /api part of the url is stripped since “api” is only used for triaging. The next few lines are pretty standard. They basically send along the original headers.

Voila! When I built and up’d my docker-compose services, I can see the blog posts showing up just like before. However, when I randomly tested image upload, I got a Request Entity too Large 413 error in the browser console. Apparently this is caused by the new nginx config, but how?

After some Googling, it turns out that nginx has a setting in HTTP server called client_max_body_size, which defaults to 1M. What’s more, from the documentation it says any request with body larger than this limit will get a 413 error, and this error cannot be properly displayed by browsers! Okay… so in the server block I added a

client_max_body_size 8m;

and everything works just fine 👌.


For the docker volume configuration, I came up with this requirement for myself because my mongodb data had been stored in the docker container – it gets lost when the container is removed. To back up the data easier and to have more confidence in the data persistency, I wanted to mount the database content from the host machine instead.

So in my docker-compose.yml,I added this simple thing to the mongodb service:

      - /data/db:/data/db

Yes two lines and I included in my post. What can you do about it 🤨

Now I can just log in my production machine and copy away the /data/db directory to back up my precious blog posts data 🙂



Use NGINX As A Reverse Proxy To Your Containerized Docker Applications