Amir XYZ

Using an nginx docker container for serving frontend and backend under the same port

October 09, 2019

Instead of installing nginx system-wide, use a docker container.

tl;dr:

# docker-compose.yml
version: "3.7"

services:
  nginx:
    image: nginx
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    network_mode: "host"  # this is important

  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD: example
      POSTGRES_DB: mynicedb
    ports:
      - "5432:5432"
    volumes:
      - ./pgdata:/var/lib/postgresql/data

  adminer:
    # this is a piece of shit but I copied the original compose file from
    # the poostgres docker hub page and it should work, but it doesn't
    image: adminer
    ports:
      - 8090:8080

And the nginx.conf:

server {
    listen 1414;
    server_name _;

    root /var/www/;
    index index.html;

    location / {
        proxy_pass http://localhost:3000;  # frontend stuff
    }

    location /back/ {
        proxy_pass http://localhost:8000;  # backend stuff
    }

    location /ws/ {  # in case you use websockets (like me!)
        proxy_pass http://localhost:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
    }
}

Some paragraphs and words

I don't like installing nginx system-wide just for the purpose of serving the full stack under the same host/port, so let's use docker. The key is having the network_mode: "host" line in the docker-compose file. This makes it easier for the nginx container to access the running apps under ports 3000 and 8000.

Initially I tried without this network mode, binding ports 1414, 3000, and 8000 to the nginx container, but when upping the containers, the frontend and backend were already bound to the npm start and ./manage.py runserver processes running in the background T_T so it died. t The only catch was the backend URL prefix, which in this example I named /back. I am not sure how, or if it's possible, to proxy_pass to the other proxy based on a 404 from the former, say, the frontend proxy. Instead of going crazy over this I just added prefixes which seemed sane to me. It can be anything from /back to /api to /ws to /secretgrandma. Just make sure your route configuration on the backend matches or you will die.

WHY T_T

Because I wanted to use cookies from JavaScript to be sent to the Django backend, and because of the same-origin policy you have to have the same (host and) port.

What I really wanted to do is authenticate from the Next.js app somehow and then have a nice cookie. With that thing I could connect to /ws and have myself authenticated amazingly. I'm not sure if I'm on the right track as my Django-foo is not great and I might be doing nonsense. The nginx trick is cool though.

Extras

Today is Yom Kippur for the Jews. I don't keep anything like that. I'm sorry (pun intended).