Wednesday, September 17, 2025

Document for Docker + nginx + HTTPS setup for Company Recommender

Docker + nginx + HTTPS setup for Company Recommender

This document contains a ready-to-use Docker deployment for your FastAPI backend and Angular frontend, plus an NGINX reverse proxy. It also includes instructions for obtaining Let’s Encrypt certificates with Certbot (manual step).

Important: I placed all files and config examples below. Follow the numbered steps in Deployment to build, obtain certificates, and run in production.


Files included

  • docker-compose.yml — orchestrates backend, frontend, nginx, and certbot (optional)

  • backend/Dockerfile — builds your FastAPI app (uvicorn)

  • frontend/Dockerfile — builds Angular production bundle and serves using nginx

  • nginx/nginx.conf — main nginx config with HTTP -> HTTPS redirect

  • nginx/conf.d/immai.acintia.com.conf — site config (reverse proxy to backend + static hosting for frontend)

  • README_DEPLOY.md — deployment steps and Certbot instructions


docker-compose.yml

version: '3.8'
services:
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: company_recommender_backend
    environment:
      - PORT=8000
      - OLLAMA_URL=https://immai.acintia.com
    expose:
      - "8000"
    restart: unless-stopped

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: company_recommender_frontend
    restart: unless-stopped
    expose:
      - "80"

  nginx:
    image: nginx:stable
    container_name: company_recommender_nginx
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - frontend
      - backend
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./certs:/etc/letsencrypt/live:ro
      - ./nginx/html:/usr/share/nginx/html:ro
    restart: unless-stopped

  certbot:
    image: certbot/certbot
    container_name: company_recommender_certbot
    volumes:
      - ./certs:/etc/letsencrypt/live
      - ./nginx/html:/var/www/html
    entrypoint: ''
    command: "/bin/sh -c 'sleep infinity'"
    restart: 'no'

networks:
  default:
    driver: bridge

Notes:

  • certbot here is present to allow you to run one-off cert issuance commands using the certbot container (instructions below).

  • ./certs will hold the live certificate files after you create them (mounted read-only into nginx).


backend/Dockerfile

# backend/Dockerfile
FROM python:3.11-slim

WORKDIR /app

# system deps (if needed)
RUN apt-get update && apt-get install -y --no-install-recommends build-essential curl && rm -rf /var/lib/apt/lists/*

COPY backend/requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY backend/ .

ENV PORT=8000

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"]

Make sure backend/requirements.txt contains: fastapi, uvicorn[standard], langgraph, langchain-core, langchain-community, pydantic, and any other packages your main.py imports.


frontend/Dockerfile (Angular)

# frontend/Dockerfile
# Build stage
FROM node:20 AS build
WORKDIR /app
COPY frontend/package*.json ./
RUN npm ci --legacy-peer-deps
COPY frontend/ .
RUN npm run build -- --configuration production

# Production stage
FROM nginx:stable
COPY --from=build /app/dist/ /usr/share/nginx/html/
# optional: copy a custom nginx conf for serving SPA (404 -> index.html handled by nginx conf)
COPY nginx/spa.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Assumes your Angular build output lands in /app/dist/ (adjust if your project name differs; ng build by default creates dist/<project-name>).


nginx/nginx.conf

user  nginx;
worker_processes  auto;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events { worker_connections 1024; }

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    include /etc/nginx/conf.d/*.conf;
}

nginx/conf.d/immai.acintia.com.conf

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name immai.acintia.com;

    root /var/www/html;

    location /.well-known/acme-challenge/ {
        alias /var/www/html/.well-known/acme-challenge/;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

# HTTPS server
server {
    listen 443 ssl http2;
    server_name immai.acintia.com;

    ssl_certificate /etc/letsencrypt/live/immai.acintia.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/immai.acintia.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    # Serve frontend static files
    location / {
        proxy_pass http://frontend:80;
        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;
    }

    # API proxying
    location /recommend {
        proxy_pass http://backend:8000/recommend;
        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;
    }

    location /chat {
        proxy_pass http://backend:8000/chat;
        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;
    }

    # optionally allow access to certbot challenge files served from nginx html
    location ^~ /.well-known/acme-challenge/ {
        alias /var/www/html/.well-known/acme-challenge/;
        allow all;
    }
}

The proxy_pass uses the Docker service names frontend and backend defined in docker-compose.yml so nginx communicates over the Docker network.


nginx/spa.conf (used by frontend image)

server {
  listen 80;
  server_name _;
  root /usr/share/nginx/html;
  index index.html;

  location / {
    try_files $uri $uri/ /index.html;
  }
}

README_DEPLOY.md — Deployment steps (summary)

  1. Clone repo and place the backend and frontend project folders side-by-side with docker-compose.yml and nginx/ folder.

project-root/
  ├─ backend/        # your FastAPI app (main.py, requirements.txt)
  ├─ frontend/       # your Angular project (package.json, angular.json)
  ├─ nginx/
  ├─ docker-compose.yml
  └─ certs/          # created after certbot
  1. Build images and start containers (without certs yet):

docker compose up -d --build
  1. Obtain Let’s Encrypt certificates using the certbot container + webroot method (run on the host):

  • Ensure DNS for immai.acintia.com points to the server IP.

  • Ensure port 80 is reachable externally and not blocked by firewall.

Run this command (example using docker exec into certbot container):

# create the directory for challenge files
mkdir -p nginx/html/.well-known/acme-challenge

# run certbot interactively to obtain certs
docker run --rm -it \
  -v "$(pwd)/certs:/etc/letsencrypt/live" \
  -v "$(pwd)/nginx/html:/var/www/html" \
  certbot/certbot certonly --webroot \
    --webroot-path /var/www/html \
    -d immai.acintia.com \
    --email your-email@example.com --agree-tos --non-interactive

If successful, certificate files will be in ./certs/immai.acintia.com/ and will be mounted into the nginx container.

  1. Reload nginx to pick up the certificates:

docker compose restart nginx
  1. (Optional) Set up automatic renewal (cron on host) using certbot renew and reload nginx after renewal.

Local dev alternative (self-signed)

If you don't want to use certbot yet, you can create a self-signed cert and mount it into ./certs with the same filenames fullchain.pem and privkey.pem for testing.


Additional operational notes

  • If you use Cloudflare, you may prefer to enable Cloudflare proxy and use their TLS — in that case you'd point nginx to use Cloudflare origin certs or use ssl_certificate accordingly.

  • Make sure your backend FastAPI app binds to 0.0.0.0 (the provided Dockerfile uses that). If your FastAPI uvicorn.run call uses 127.0.0.1, update it.

  • In production, consider using environment variables or a .env file for secrets and configuration. Also increase uvicorn worker count appropriately.

  • Monitor logs: docker compose logs -f nginx and docker compose logs -f backend.


Troubleshooting

  • Certbot fails: ensure port 80 isn't blocked and DNS resolves. Run certbot with --staging to test.

  • Backend 502 from nginx: check the proxy_pass host/port match your compose service names and ports; use docker compose ps to verify.


If you want, I can also:

  • Provide a .env and systemd unit file for auto-start on server boot.

  • Add a healthcheck to the backend service and change nginx config to use proxy_pass http://backend:8000; more generally.


End of file.

No comments:

Post a Comment

Document for Docker + nginx + HTTPS setup for Company Recommender

Docker + nginx + HTTPS setup for Company Recommender This document contains a ready-to-use Docker deployment for your FastAPI backend and An...