Linux
This section describes how to deploy Canvus server 26.4.0 using containers on Linux. Linux is the only supported platform for production Canvus server deployments; the combined, postgres, and media images are all Linux containers.
Note
Supported distributions: Ubuntu 22.04+, Debian 12+, RHEL 9+, Fedora 38+, CentOS Stream 9+.
System requirements
- RAM: 4 GB minimum, 8 GB recommended. The web-proxy gateway runs on Node.js 20 LTS; under load the combined container needs headroom beyond the C++ server itself. Running below 4 GB has been observed to trigger out-of-memory kills.
- 2 CPU cores minimum (4 recommended)
- 20 GB disk space minimum (50 GB recommended for production with asset uploads)
- Internet connection for initial image download
- UDP port range 40000-40100 reachable from clients if WebRTC video streaming is enabled (default)
Install Podman
Ubuntu / Debian:
sudo apt update
sudo apt install -y podman podman-compose
RHEL / Fedora / CentOS Stream:
sudo dnf install -y podman podman-compose
Verify:
podman --version
# Should show podman version 3.0+ (4.0+ recommended)
Login to the container registry
Canvus Server images are hosted on the MultiTaction container registry.
sudo podman login docker.multitaction.com -u canvus-deploy -p gldt-synTX_NnF8LcmnktR1xK
Note
sudo is required for all Podman commands. Podman uses separate container namespaces for root and non-root users. Since sudo is needed for privileged port binding (ports 80/443), all Podman commands must use sudo consistently --- mixing sudo podman ... and podman ... will make containers appear missing.
Download the compose file
wget https://canvus-downloads.multitaction.com/server/podman-compose.yml
Note
In 26.4.0 the compose file download host moved from the S3 bucket to canvus-downloads.multitaction.com (served via Cloudflare R2). Old canvus-downloads.s3.amazonaws.com links will be retired.
What's in the compose file
The 26.4.0 compose file deploys three services on an internal bridge network (canvus-network):
postgres(container namecanvus-postgres) --- PostgreSQL 17 with the Canvus schema. Listens oncanvus-network:5432only; no host port is published.canvus(container namecanvus-combined) --- the C++ server and the Node.js web-proxy gateway. The web-proxy listens internally on port 5805 and serves the unified React web-client directly. Publishes host ports 80 (HTTP redirect → 443) and 443 (HTTPS).canvus-media(container namecanvus-media) --- mediasoup SFU for WebRTC and FFmpeg for HLS transcoding. Publishes the UDP range40000-40100for WebRTC media.canvus-combinedrelays WebRTC signaling to this service overws://canvus-media:4443/ws?peerId=server-relay.
The canvus-media service is started by default in 26.4.0 because CANVUS_WEBRTC_ENABLED=true is the shipped default. If you disable WebRTC, you can stop or remove the canvus-media service from the compose file.
Image paths (all at tag 26.4.0; latest always points at the most recent release):
docker.multitaction.com/swrd/conan/canvus/canvus-server/combined:26.4.0docker.multitaction.com/swrd/conan/canvus/canvus-server/postgres:26.4.0docker.multitaction.com/swrd/conan/canvus/canvus-server/media:26.4.0
Configure the deployment
Edit podman-compose.yml before starting. At minimum, change these values:
In the canvus service:
environment:
CANVUS_EXTERNAL_URL: https://canvus.example.com
CANVUS_ADMIN_EMAIL: admin@yourcompany.com
CANVUS_ADMIN_PASSWORD: YourSecurePassword123!
POSTGRES_PASSWORD: a-strong-database-password
In the postgres service (must match the password above):
environment:
POSTGRES_PASSWORD: a-strong-database-password
For testing, https://localhost works as the external URL. The server generates a self-signed certificate automatically.
WebRTC configuration
The canvus service ships with:
CANVUS_WEBRTC_ENABLED: "true"
CANVUS_WEBRTC_MEDIA_URL: ws://canvus-media:4443/ws?peerId=server-relay
The ?peerId=server-relay query parameter is required --- the SFU rejects signaling connections that omit it.
If your server sits behind NAT (typical production deployment), set ANNOUNCED_IP on the canvus-media service to the host's public IP so WebRTC peers can reach the SFU:
canvus-media:
environment:
ANNOUNCED_IP: 203.0.113.1
Create data directory
sudo mkdir -p /canvus-data
The containers create these subdirectories automatically on first start:
/canvus-data/config/--- server configuration (mt-canvus-server.ini,.session-secret)/canvus-data/certs/--- TLS certificates (self-signed auto-generated on first start)/canvus-data/assets/--- uploaded canvas content (images, PDFs, videos)/canvus-data/backups/--- server and database backups/canvus-data/logs/--- server and web-proxy logs/canvus-data/licenses/--- Canvus license files (.cslicense)/canvus-data/mipmaps/--- pre-computed image mipmaps/canvus-data/postgres/--- PostgreSQL data directory
The web-proxy gateway persists its session secret at /canvus-data/config/.session-secret so login sessions survive container restarts.
Start services
sudo podman-compose up -d
Note
sudo is required for two reasons:
- Privileged port binding --- rootless Podman cannot map host ports below 1024 (ports 80 and 443). External Canvus clients require HTTPS on port 443.
- Namespace consistency --- containers started with
sudoare only visible tosudocommands.
The first startup takes longer because it pulls three images, generates a TLS certificate, creates the database schema, and creates the admin user. Subsequent starts complete in about 30-60 seconds.
Verify
Check that containers are running:
sudo podman-compose ps
You should see canvus-postgres, canvus-combined, and canvus-media all running. The health check for canvus-combined may take up to 2 minutes on first start.
Open https://localhost in your browser. The request lands on the web-proxy gateway (port 5805 inside the container), which serves the unified React web-client directly. Accept the self-signed certificate warning and login with the admin credentials you set above.
Landing-page behaviour
In 26.4.0 the entry point at https://<your-server>/ is the Node.js web-proxy gateway (port 5805 inside the container). The web-proxy serves the unified React web-client (SPA) directly for all web requests, while routing REST API calls, WebSocket endpoints, and WebRTC signaling through to the C++ server.
Backup, restore, and migration
See the configuration reference for full backup/restore procedures, including filesystem backups, the server binary's --backup/--restore commands, and migrating from bare-metal installations. A single sudo tar -czf backup.tar.gz /canvus-data/ captures all state.
SSL certificates
Self-signed (default): The server generates a self-signed certificate on first startup. Browsers will show a security warning.
Production certificates: Place your CA-signed certificates in /canvus-data/certs/ before starting:
sudo cp your-certificate.pem /canvus-data/certs/server.cert.pem
sudo cp your-private-key.pem /canvus-data/certs/server.key.pem
sudo cp your-chain.pem /canvus-data/certs/server.chain.pem
Then start (or restart) the containers.
License activation
Via dashboard (recommended):
- Navigate to
https://your-server/admin/settings/license - Login with admin credentials
- Enter your activation key and click Activate
Via environment variable:
Set CANVUS_LICENSE_KEY in the canvus service before first startup.
Offline activation:
Place .cslicense files in /canvus-data/licenses/ and restart.
Management
# Start / stop / restart
sudo podman-compose up -d
sudo podman-compose stop
sudo podman-compose restart
# View logs
sudo podman-compose logs -f canvus-combined
sudo podman-compose logs -f canvus-media
sudo podman-compose logs -f canvus-postgres
# Check status
sudo podman-compose ps
# Update to latest version
sudo podman-compose pull
sudo podman-compose down
sudo podman-compose up -d
Image tags and CI builds
Released versions are tagged by Canvus version (e.g. 26.4.0) and latest. Development builds from merge requests are tagged <version>-mr<IID> (for example, 26.4.0-mr24). MR builds never overwrite latest --- pulling :latest always gives you the current released version. To test a specific MR, pull the MR-tagged image explicitly.
Behind a reverse proxy
If another service (Traefik, nginx) already uses ports 80/443, use unprivileged ports:
environment:
CANVUS_HTTPS_PORT: 8443
CANVUS_EXTERNAL_URL: https://canvus.example.com
ports:
- "8080:80" # HTTP redirect (always port 80 inside container)
- "8443:8443" # HTTPS (matches CANVUS_HTTPS_PORT)
Note
The HTTP-to-HTTPS redirect always listens on port 80 inside the container. The left side of the port mapping controls the host port.
Configure your reverse proxy to forward to these ports. Do not proxy the UDP 40000-40100 range --- WebRTC media must reach canvus-media directly; forward those UDP ports at the firewall instead.
Troubleshooting
Port already in use:
sudo ss -tlnp | grep :443
Stop the conflicting service or use unprivileged ports.
Permission denied on /canvus-data:
sudo chown -R $(id -u):$(id -g) /canvus-data
On RHEL/Fedora/CentOS, the :Z suffix on volume mounts handles SELinux relabeling automatically. The :Z suffix is safely ignored on Ubuntu/Debian.
Container not found:
If podman ps shows nothing but sudo podman ps shows your containers, you have a namespace mismatch. Use sudo consistently.
Database connection issues:
sudo podman exec -it canvus-postgres pg_isready -U canvus -d canvus
WebRTC video not working:
# Confirm canvus-media is healthy and the SFU is listening
sudo podman-compose logs canvus-media | grep -i "sfu\|listening\|ready"
# Confirm server-relay signaling connected from canvus-combined
sudo podman-compose logs canvus-combined | grep -i webrtc
If clients outside the host cannot receive media, check that UDP 40000-40100 is open at the firewall and that ANNOUNCED_IP on canvus-media matches the host's reachable IP.
Combined container OOM-killed:
Increase host RAM or the container memory limit. Canvus 26.4.0 requires 4 GB minimum and 8 GB is recommended for production; the Node.js web-proxy process is sensitive to memory pressure.