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.
| Variable | Description | Default Value |
|---|---|---|
DB_PASSWORD | Secure root password for the PostgreSQL database container. | your_secure_db_password_here |
SRT_URL | Hostname or IP address for SRT video streaming endpoints. | localhost |
API_URL | Full publicly accessible URL pointing to the backend API. | http://localhost:4000/api |
JWT_SECRET | Your JWT secret. | your_jwt_secret_here |
JWT_EXPIRES_IN | The JWT expiration configuration | 7d |
To set up your environment:
- Copy the template file:
cp .env.example .env - Open the newly created
.envfile 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
| Variable | Description | Value / Default |
|---|---|---|
PORT | The internal port that the Node.js backend listens on. | 4000 |
DB_HOST | The internal Docker network hostname used by the backend to reach the database container. | fov-db |
DB_PORT | The port number used for database communication. | 5432 |
DB_USER | The database user profile name. | root |
SRT_PORT | The primary base port allocated for incoming SRT stream ingestion. | 9999 |
DB_NAME | The exact target database name. | fovwebdb |
FFMPEG_PATH | The absolute path inside the container where FFmpeg is installed, essential for processing video feeds. | /usr/local/bin/ffmpeg |
MEDIA_ROOT | The container directory mapped to persistent storage where media assets and HLS segments are saved. | /var/media |
Database Container (fov-db) Variables
| Variable | Description | Value / Default |
|---|---|---|
POSTGRES_DB | Instructs the official PostgreSQL container to automatically create a database upon initial startup. | fovwebdb |
POSTGRES_USER | Defines the administrative user account created for the database instance. | root |
Frontend Build Arguments
| Argument | Description | Source / Context |
|---|---|---|
API_URL | The 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 / Mount | Type | Target Container | Purpose |
|---|---|---|---|
./backend/media:/var/media | Bind Mount | Backend | Stores persistent media assets and HLS segments generated by the backend. |
fov-db:/var/lib/postgresql | Named Volume | Database (fov-db) | Ensures PostgreSQL database records persist safely across container restarts. |
./fovwebdb.sql:/docker-entrypoint-initdb.d/init.sql | Bind Mount | Database (fov-db) | Automatically seeds the database schema and initial data on first-time container initialization. |
Networking
| Network Name | Driver / Type | Purpose |
|---|---|---|
frontend | Bridge | Connects the frontend container to the backend API service to allow user web traffic communication. |
backend | Bridge (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-network | External 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:
- API Backend: http://localhost:4000
- Stream Ingest (SRT): Ports
9999-10010(available on both TCP and UDP) - Frontend Web App: http://localhost:4200
- PostgreSQL Database:
localhost:5432
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.
- Source Domain: Your public frontend URL (e.g.,
-
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.
- Source Domain: Your public API URL (e.g.,
production behavior summary
- Public web traffic enters exclusively through Nginx Proxy Manager on ports
80and443. - 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 to127.0.0.1and are completely shielded from public exposure. - Stream ingestion ports (
9999-10010) remain publicly accessible for your SRT streaming sources.