Nginx as a reverse proxy in front of a container

A reverse proxy is nginx receiving requests on ports 80 and 443, under your domain, and handing them to the application sitting on another port inside the server. It is the normal way to put a container on the internet: the application stays hidden on 127.0.0.1, and nginx deals with the domain and with HTTPS. This article is for a VPS of your own, with Ubuntu 22.04 LTS and the application already running in a container (Compose example).

Setting up the proxy

1 Point the name at the VPS. Create an A record for, say, app.asuaempresa.co.mz pointing to the VPS IP, and wait for it to propagate: pointing a domain at your VPS.
2 Publish the application on the server itself only. In Compose: "127.0.0.1:3000:3000". That way only nginx can reach it.
3 Install nginx: sudo apt install nginx.
4 Create the site file, with sudo nano /etc/nginx/sites-available/app:server {
    listen 80;
    server_name app.asuaempresa.co.mz;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
5 Enable, test and reload: sudo ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/app
sudo nginx -t
sudo systemctl reload nginx
The nginx -t checks the syntax before reloading. If it says “syntax is ok”, go ahead and reload.
6 Open ports 80 and 443 in the firewall: sudo ufw allow 80/tcp and sudo ufw allow 443/tcp. See ports and firewall.
7 Add HTTPS with certbot: HTTPS for a container.

The errors you will meet

What you see Usual cause What to do
502 Bad Gateway nginx cannot talk to the application: the container is stopped, or the port is wrong. docker ps, and check the port in proxy_pass.
504 Gateway Time-out The application takes longer than nginx is willing to wait. Look at what the application is doing and at its logs. Only then think of raising nginx’s timeouts.
The nginx welcome page The request did not match server_name, or the default site is still enabled. Check the name and the DNS. Remove the default site: sudo rm /etc/nginx/sites-enabled/default (it is only the link).
413 Request Entity Too Large An upload larger than nginx accepts. Add client_max_body_size in the server block with the size you need.
Redirect loop The application does not know the visitor came over HTTPS. Check X-Forwarded-Proto and set the application to respect it.

nginx’s error logs are in /var/log/nginx/error.log. If the application uses websockets (live chat, dashboards that update by themselves), add these lines to the location block: proxy_http_version 1.1;, proxy_set_header Upgrade $http_upgrade; and proxy_set_header Connection "upgrade";.

Do not leave the application open on a public port as well. If the container publishes 3000:3000 (without the 127.0.0.1), Docker opens that port to everyone, even if ufw says otherwise, and the proxy becomes decoration. It is the most frequent flaw in this design.
You can also use Caddy or Traefik instead of nginx: they handle HTTPS for you. nginx has the advantage of being the best documented. On an unmanaged VPS, installing and maintaining any of the three is yours: how far our support goes.

The domain already points at the VPS but the server does not even answer a ping? That we check ourselves.

Open a support ticket

SEE ALSO

HTTPS for a container: a certificate and a domain

Ports and firewall on a VPS: why your app is not reachable

Pointing a domain at your own VPS: nameservers and glue records

More about Node.js, Python and Docker

More articles on the same subject, for when this one is not enough.

RECOMMENDED PRODUCT

Web hosting with cPanel

Domain and SSL included, daily backups and the panel you already know. from $5.36/mo (3-year plan, with coupon)

See plans
  • 0 Users Found This Useful
Was this answer helpful?