Docker Deployment (Enterprise)
The ScreenStop Dashboard is available as a Docker image for enterprise deployments, including air-gapped and containerized environments.
Current default vs. OCI-native (roadmap)
This guide describes the portable default deployment, which is platform-agnostic. For Oracle Cloud (OCI) deployments, the following are tracked on the near-term roadmap and are not reflected in the steps below:
- Secrets management: this guide stores
DASHBOARD_SECRET_KEY/STATION_API_TOKENin a.envfile. OCI Vault / KMS-backed secrets (no plaintext env vars) are the committed target for OCI deployments. - Encryption at rest: frame encryption with a pluggable key provider is available today
(
SCREENSTOP_FRAME_ENCRYPTION); OCI Vault as the key provider is the OCI-native target. - MFA: TOTP-based multi-factor authentication for admin accounts.
Treat the secrets/Vault/MFA items as incoming for the OCI release — see the CSSAP responses for the authoritative commitment and target dates.
What's Included
Each release ships a versioned Docker tar file and SBOM:
| File | Description |
|---|---|
screenstop-<version>.tar |
Docker image — load with docker load |
screenstop-<version>-sbom.json |
CycloneDX Software Bill of Materials |
You will also create two small deployment files on the host (docker-compose.yml and nginx.conf) — both are provided in Step 2 below.
Prerequisites
- Docker Engine 20.10+
- Docker Compose v2
- SSL certificate + private key (
cert.pem,key.pem)
Step 1 — Load the Image
Transfer the tar file to your server, then load it:
Verify it loaded:
Step 2 — Create the Deployment Files
In your deployment directory, create docker-compose.yml. Note image: — you run the pre-built image you loaded, you do not build from source:
services:
screenstop:
image: screenstop:2.0.2
container_name: screenstop
expose:
- "8080"
volumes:
- screenstop_data:/app/data
environment:
- DASHBOARD_SECRET_KEY=${DASHBOARD_SECRET_KEY}
- STATION_API_TOKEN=${STATION_API_TOKEN}
- SCREENSTOP_DEV_MODE=${SCREENSTOP_DEV_MODE:-false}
- DASHBOARD_DB_PATH=/app/data/dashboard.db
restart: unless-stopped
nginx:
image: nginx:alpine
container_name: screenstop-nginx
ports:
- "443:443"
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ${CERTS_DIR:-./certs}:/etc/nginx/certs:ro
depends_on:
- screenstop
restart: unless-stopped
volumes:
screenstop_data:
And nginx.conf (terminates TLS, redirects HTTP→HTTPS, rate-limits login):
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_status 429;
server {
listen 443 ssl;
ssl_certificate /etc/nginx/certs/cert.pem;
ssl_certificate_key /etc/nginx/certs/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location /auth/login {
limit_req zone=login burst=5 nodelay;
proxy_pass http://screenstop:8080;
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 https;
}
location / {
proxy_pass http://screenstop:8080;
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 https;
}
}
server {
listen 80;
return 301 https://$host$request_uri;
}
Editing nginx.conf later
nginx.conf is bind-mounted as a single file. If you edit it, run docker compose restart nginx so the change is picked up (a reload alone may keep serving the old file).
Step 3 — Prepare Certificates
Place your TLS certificate and private key in a directory on the host:
Self-signed certificate (testing only)
For non-production testing, generate a self-signed cert:
Step 4 — Configure Environment
Create a .env file alongside docker-compose.yml:
DASHBOARD_SECRET_KEY=<strong-random-secret>
STATION_API_TOKEN=<pre-shared-token-for-agents>
CERTS_DIR=/opt/screenstop/certs
| Variable | Description |
|---|---|
DASHBOARD_SECRET_KEY |
Session signing secret — generate with openssl rand -hex 32 |
STATION_API_TOKEN |
Token agents use to authenticate with the server |
CERTS_DIR |
Host path to the directory containing cert.pem and key.pem |
Step 5 — Start the Container
The dashboard will be available at https://<server-ip>/ — the root automatically redirects to the dashboard UI at /app/.
HTTP (port 80) redirects automatically to HTTPS (port 443).
Step 6 — Configure Agent Endpoints
On each protected workstation, set the dashboard URL in setup_parameters.json (place this file in the install directory before or after deployment — IT/MDM):
{
"dashboard_url": {
"value": "https://<server-ip>",
"override": true
},
"dashboard_api_token": {
"value": "<pre-shared-token>",
"override": true
}
}
The agent picks up the file automatically on next startup.
Step 7 — Bootstrap Admin Account
On a fresh deployment the database has no users. Dev mode provides a temporary built-in admin/admin login used once to create your real admin account — you then change it and turn dev mode off. ScreenStop never ships or transmits a password; you set your own here.
Do this immediately — before exposing the dashboard
The temporary admin/admin login is active only while dev mode is on. Perform this bootstrap right after deployment, on a closed/internal network, before the dashboard is reachable by users. Then:
- Create your real admin with a strong password (Step 3 below), and
- Disable dev mode (Step 4) so
admin/adminno longer works.
Never leave the dashboard exposed with dev mode on — anyone who reaches it could log in as admin/admin.
1. Start with dev mode enabled (temporary):
In your .env file, add:
Restart the container:
2. Log in at https://<server-ip>/ (redirects to the dashboard at /app/) with the temporary credentials:
| Username | Password |
|---|---|
admin |
admin |
3. Go to Admin → Users → Create User and create your real admin account:
- Username must be a valid email address (e.g.
admin@yourcompany.com) - Password must be at least 12 characters with uppercase, lowercase, a number, and a special character
4. Disable dev mode — remove SCREENSTOP_DEV_MODE=true from .env and restart:
Warning
Never leave SCREENSTOP_DEV_MODE=true in production. It bypasses the database entirely and allows login with admin/admin.
Persistent Data
Station data, events, and configuration are stored in a named Docker volume (screenstop_data). This persists across container restarts and upgrades.
To back up:
docker run --rm \
-v screenstop_data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/screenstop-backup-$(date +%Y%m%d).tar.gz /data
Upgrading
Each new release is delivered as a release bundle containing the image tar, a matching docker-compose.yml, a checksum, the upgrade script, and these notes.
Dashboard — recommended (scripted, with automatic rollback)
From your deployment directory, run the bundled script with the new image tar:
The script will:
- Back up the database (and the current
docker-compose.yml) to./backups/ - Verify the image checksum (if
screenstop-<new-version>.tar.sha256is present) - Load the new image and point Compose at the new version
- Restart the stack — your data volume is preserved and database migrations run automatically
- Health-check the dashboard, and automatically roll back to the previous version if the new one fails to start cleanly
Configurable paths
The script reads sensible defaults but every path is overridable via environment variables (SCREENSTOP_DEPLOY_DIR, SCREENSTOP_COMPOSE_FILE, SCREENSTOP_HEALTH_URL, …) for non-standard installs.
Dashboard — manual fallback
If you prefer to upgrade by hand:
# 1. Back up the database first
docker cp screenstop:/app/data/dashboard.db ./dashboard-backup-$(date +%F).db
# 2. Load the new image
docker load < screenstop-<new-version>.tar
# 3. Point docker-compose.yml at the new image tag (use the bundled compose file)
# 4. Restart
docker compose down && docker compose up -d
The data volume is preserved automatically and schema migrations run on startup.
Agent (workstations)
The endpoint agent is updated the same way it was deployed — push the new agent installer through your MDM (Intune, Jamf, SCCM, Kandji, …). The new installer reinstalls over the old agent and preserves the existing setup_parameters.json (dashboard URL + token), so no per-machine reconfiguration is needed. See Install on Windows / Install on macOS for the MDM deployment steps.
Upgrade order
Upgrade the dashboard first, then roll out the agent update. The dashboard is backward-compatible with the previous agent version, so a brief version skew during rollout is safe.
Security Notes
- The dashboard container runs as a non-root user (
screenstop) - All communication is TLS 1.2+ (nginx terminates TLS, proxies to FastAPI internally)
- Port 8080 is container-internal only. In
docker-compose.ymlit uses Dockerexpose:(reachable only on the internal Docker network between nginx and the app), notports:— so it is never published to the host or reachable from outside. Only 443/80 are host-published. - Firewall: only ports 443 and 80 need to be open inbound