Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Deploy the Web Stack Using Docker

Date: September 23rd, 2026

This guide outlines how to deploy the web application stack locally for development or in a production environment using Docker and Docker Compose.

Prerequisites

1. Docker Installed

Ensure you have Docker and Docker Compose installed on your system.

2. Configure the Environment

At the root of the repository, you will find a .env.example file. This file contains the default environment variables required by the application.

VariableDescriptionDefault Value
DB_PASSWORDSecure root password for the PostgreSQL database container.your_secure_db_password_here
SRT_URLHostname or IP address for SRT video streaming endpoints.localhost
API_URLFull publicly accessible URL pointing to the backend API.http://localhost:4000/api
JWT_SECRETYour JWT secret.your_jwt_secret_here
JWT_EXPIRES_INThe JWT expiration configuration7d

To set up your environment:

  1. Copy the template file: cp .env.example .env
  2. Open the newly created .env file and customize the values according to your environment.

3. Internal Architecture (Docker Compose)

In addition to environment variables and build arguments, the docker-compose.yml file configures internal variables, persistent storage volumes, and network isolation to wire the architecture together safely:

Backend Container Variables

VariableDescriptionValue / Default
PORTThe internal port that the Node.js backend listens on.4000
DB_HOSTThe internal Docker network hostname used by the backend to reach the database container.fov-db
DB_PORTThe port number used for database communication.5432
DB_USERThe database user profile name.root
SRT_PORTThe primary base port allocated for incoming SRT stream ingestion.9999
DB_NAMEThe exact target database name.fovwebdb
FFMPEG_PATHThe absolute path inside the container where FFmpeg is installed, essential for processing video feeds./usr/local/bin/ffmpeg
MEDIA_ROOTThe container directory mapped to persistent storage where media assets and HLS segments are saved./var/media

Database Container (fov-db) Variables

VariableDescriptionValue / Default
POSTGRES_DBInstructs the official PostgreSQL container to automatically create a database upon initial startup.fovwebdb
POSTGRES_USERDefines the administrative user account created for the database instance.root

Frontend Build Arguments

ArgumentDescriptionSource / Context
API_URLThe full URL pointing to the backend API, injected into the Angular frontend container during build time.Passed from ${API_URL} in .env

Volumes & Storage

Volume / MountTypeTarget ContainerPurpose
./backend/media:/var/mediaBind MountBackendStores persistent media assets and HLS segments generated by the backend.
fov-db:/var/lib/postgresqlNamed VolumeDatabase (fov-db)Ensures PostgreSQL database records persist safely across container restarts.
./fovwebdb.sql:/docker-entrypoint-initdb.d/init.sqlBind MountDatabase (fov-db)Automatically seeds the database schema and initial data on first-time container initialization.

Networking

Network NameDriver / TypePurpose
frontendBridgeConnects the frontend container to the backend API service to allow user web traffic communication.
backendBridge (internal: true)An isolated internal network ensuring the PostgreSQL database is completely hidden from external access and reachable exclusively by the backend service.
nginx-proxy-networkExternal Bridge(Production only) Used to route traffic securely through an external reverse proxy (like Nginx Proxy Manager) when using the production override.

4. Personalizing Docker Compose (Optional)

While optional and generally not recommended for standard setups, you can modify the default ports or volumes directly within the docker-compose.yml file to fit custom infrastructure requirements.

scaling ingest streams

You can adjust the port range for stream ingestion (default: 9999-10010) in your Docker Compose file. Changing this range directly dictates the maximum number of simultaneous, distinct streams your server can handle at the same time.


Local Development Deployment

To quickly spin up the application for development, you can use the provided automation script or run the manual commands below.

Option A: Using the Script

Run the local deployment script from your terminal:

./deploy-local.sh

Option B: Manual Deployment

Execute the following commands to create necessary media directories, assign proper permissions, and build the stack:

mkdir -p ./backend/media/hls
chmod -R 777 ./backend/media
docker compose up --build

Local Endpoints Overview

With the default configuration, the containers will expose the following services locally:

warning

This configuration binds services directly to your host machine and is not recommended for a secure production environment.


Production Deployment Example With Nginx Proxy Manager

For production environments, we recommend running the application stack behind Nginx Proxy Manager (NPM). This ensures traffic is handled securely over standard HTTP/HTTPS ports (80/443) while isolating internal database and container ports.

hardware and bandwidth considerations

Because this project functions as a video streaming platform, ensure your production server is properly provisioned.

High Network Bandwidth: Ingesting live video feeds and serving HLS chunks to multiple concurrent viewers consumes significant inbound and outbound network traffic.

Capable Machine: Each active live stream spins up a dedicated FFmpeg process on the backend to process and package the video feed. Plan your maximum concurrent stream limits around your server specifications.

Step 1: Create the Shared External Network

Before launching your services, create the external Docker network that NPM and your application stack will share:

docker network create nginx-proxy-network

Step 2: Deploy Nginx Proxy Manager

Deploy your Nginx Proxy Manager stack, making sure it is connected to the nginx-proxy-network:

services:
  app:
    image: 'jc21/nginx-proxy-manager:2.15.1'
    restart: unless-stopped
    ports:
      - '80:80'   # Public HTTP Port
      - '443:443' # Public HTTPS Port
      - '81:81'   # Admin Web Port
    environment:
      TZ: "Europe/Paris"
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    networks:
      - nginx-proxy-network

networks:
  nginx-proxy-network:
    external: true

Step 3: Deploy the Application Stack with Production Overrides

Update your .env file with your production domain names and valid URL endpoints, then deploy using the production compose override configuration:

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build

The docker-compose.prod.yml file restricts public ports to loopback/SRT streams and joins the nginx-proxy-network. You can customize it to your needs:

# docker-compose.prod.yml
services:
  backend:
    ports: !override
      - "127.0.0.1:4001:4000"
      - "0.0.0.0:9999-10010:9999-10010"
      - "0.0.0.0:9999-10010:9999-10010/udp"
    networks:
      - nginx-proxy-network

  frontend:
    ports: !override
      - "127.0.0.1:4201:80"
    networks:
      - nginx-proxy-network

  fov-db:
    ports: !override
      - "127.0.0.1:5432:5432"

networks:
  nginx-proxy-network:
    external: true

Step 4: Configure Proxy Hosts in Nginx Proxy Manager

Log into your Nginx Proxy Manager admin panel (at port 81) and map your domains using the internal container names:

  • Frontend Proxy Host:

    • Source Domain: Your public frontend URL (e.g., app.yourdomain.com)
    • Forward Hostname / IP: frontend
    • Forward Port: 80
    • SSL: Request a Let’s Encrypt SSL Certificate and enable Force SSL.
  • Backend API Proxy Host:

    • Source Domain: Your public API URL (e.g., api.yourdomain.com)
    • Forward Hostname / IP: backend
    • Forward Port: 4000
    • SSL: Request a Let’s Encrypt SSL Certificate and enable Force SSL.

production behavior summary

  • Public web traffic enters exclusively through Nginx Proxy Manager on ports 80 and 443.
  • NPM routes requests internally to containers via the shared nginx-proxy-network.
  • The database (fov-db) and local management ports (4001, 4201, 5432) are safely bound exclusively to 127.0.0.1 and are completely shielded from public exposure.
  • Stream ingestion ports (9999-10010) remain publicly accessible for your SRT streaming sources.