Contigov – Deployment Guide

This guide covers everything you need to deploy the Contigov server, connect agents, and go live in production. Follow the sections in order for the smoothest experience.

 

Table of Contents

  1. Architecture Overview
  2. Prerequisites
  3. Generate Required Secrets
  4. Configure the .env File
  5. Deployment Options
  6. First Login & Initial Setup
  7. Deploying Agents
  8. Health & Readiness Checks
  9. Database Backups
  10. Upgrading
  11. Troubleshooting
 

1. Architecture Overview

Contigov consists of two components:

  • Central Server — a Node.js/Express application with an AES-256-GCM encrypted SQLite database, served as a Docker container. It provides the web UI, REST API, policy engine, and audit log.
  • Agent — a compiled Go binary that runs natively on each managed endpoint (Windows, macOS, Linux). It reports container and image data, enforces policy via the system hosts file, intercepts blocked pull attempts, and runs Trivy vulnerability scans.

Agents communicate with the server over HTTPS (or HTTP in proxy mode behind a load balancer). The server supports up to 1,000 simultaneous agents on a single instance.

 

2. Prerequisites

Server host

  • Docker Engine 20.10+ (or Docker Desktop)
  • 1 GB RAM minimum (2 GB recommended for fleets > 100 agents)
  • 10 GB disk (database grows with fleet size and scan results)
  • Ports 443 and 80 open for direct TLS, or 3443 open to your load balancer for proxy mode

Managed endpoints (agents)

  • Docker or Podman installed and running
  • Network access to the Contigov server URL
  • Administrator / root privileges (for hosts file enforcement)
 

3. Generate Required Secrets

Three secrets must be created before starting the server. Run these commands on any machine that has OpenSSL installed.

On Linux / macOS

# JWT signing key — signs all user session tokens
openssl rand -hex 32

# Database encryption key — AES-256-GCM, encrypts the SQLite file at rest
openssl rand -hex 32

# Agent API key — shared secret between server and all agents
openssl rand -hex 16

On Windows (PowerShell)

# Run once for each key — copy each output separately
[System.BitConverter]::ToString(
(1..32 | % { [byte](Get-Random -Max 256) })
).Replace(‘-‘,”).ToLower()

⚠ Important: Save all three values securely (password manager, AWS Secrets Manager, etc.). The database encryption key cannot be recovered — losing it means losing all stored data.

 

4. Configure the .env File

All runtime configuration is provided through a single .env file. Create this file on your server host before starting the container.

# ── Required ──────────────────────────────────────────────────────────────────

# Signs all JWT session tokens. Keep secret and consistent across restarts.
JWT_SECRET=your-64-char-hex-value-here

# AES-256-GCM key for encrypting the SQLite database at rest.
# WARNING: losing this key means losing all data — back it up safely.
DB_ENCRYPTION_KEY=your-64-char-hex-value-here

# Shared secret that all agents must send in the Authorization header.
# Set the same value as DOCKER_GOV_API_KEY in each agent’s config.
API_KEY=your-32-char-hex-value-here

# Enforce production mode (required secrets, secure cookies)
NODE_ENV=production

# ── Recommended ───────────────────────────────────────────────────────────────

# Comma-separated list of allowed browser origins (CORS)
ALLOWED_ORIGINS=https://your-domain.com

# Externally reachable URL of this server — written into agent config downloads
PUBLIC_SERVER_URL=https://your-domain.com

# ── TLS Mode ──────────────────────────────────────────────────────────────────
# auto = serve HTTPS directly using the built-in self-signed cert (default)
# proxy = TLS terminated by upstream load balancer (AWS ALB + ACM, nginx)
# direct = HTTPS only using your own certificate files; exits if certs missing
TLS_MODE=auto

# ── Optional ──────────────────────────────────────────────────────────────────

# Path for hourly encrypted database backups inside the container
# Mount a separate Docker volume at this path
# BACKUP_DIR=/backups

Environment variable reference

Variable Required Description
JWT_SECRET Yes Signs all user session JWTs — openssl rand -hex 32
DB_ENCRYPTION_KEY Yes AES-256-GCM database encryption key — openssl rand -hex 32
API_KEY Yes Shared agent authentication secret — openssl rand -hex 16
NODE_ENV Recommended Set to production to enforce required vars and enable secure cookies
ALLOWED_ORIGINS Recommended Comma-separated CORS origins, e.g. https://gov.company.com
PUBLIC_SERVER_URL Recommended Externally reachable URL shown in agent config downloads
TLS_MODE Optional auto (default) / proxy (ALB) / direct (custom cert)
TLS_CERT Optional Path to TLS certificate file (auto/direct modes)
TLS_KEY Optional Path to TLS private key file (auto/direct modes)
TRUST_PROXY Optional Number of upstream proxy hops (defaults to 1 when TLS_MODE=proxy)
BACKUP_DIR Optional Container path for hourly encrypted backups — mount a separate volume
PORT Optional Internal application port (default: 3443)
 

5. Deployment Options

Option A — Self-Signed Certificate (Quick Start / Internal Networks)

Best for: internal networks, evaluation, or any environment where you control what browsers connect.

The Docker image ships with a self-signed certificate baked in. Zero configuration required for TLS — just start the container.

Step 1 — Pull the image

docker pull reg.lami.co.il/contigov:latest

Step 2 — Create the data volume

docker volume create contigov-data

Step 3 — Start the container

docker run -d \
–name contigov-server \
–restart unless-stopped \
-p 443:3443 \
-p 80:3080 \
-v contigov-data:/app/data \
–env-file .env \
reg.lami.co.il/contigov:latest

Step 4 — Verify it is running

docker logs contigov-server
# You should see: Contigov server vX.X.X starting

Access the UI at https://your-server-ip. Accept the browser’s self-signed certificate warning on first visit.

Port 80 automatically redirects to HTTPS — no separate configuration needed.

 

Option B — AWS EC2 + Application Load Balancer + ACM (Recommended for AWS)

Best for: production deployments on AWS where you want a trusted public certificate managed by ACM.

In this mode the ALB handles TLS termination. The container runs plain HTTP internally and trusts X-Forwarded headers from the ALB.

Step 1 — Set TLS_MODE in your .env

TLS_MODE=proxy
NODE_ENV=production
ALLOWED_ORIGINS=https://your-domain.com
PUBLIC_SERVER_URL=https://your-domain.com

Step 2 — Start the container (HTTP only, no port 80 needed)

docker run -d \
–name contigov-server \
–restart unless-stopped \
-p 3443:3443 \
-v contigov-data:/app/data \
–env-file .env \
reg.lami.co.il/contigov:latest

Step 3 — Create an EC2 Security Group for the instance

  • Inbound: TCP 3443 from ALB security group ID
  • Inbound: TCP 22 from your IP (for SSH management)
  • Outbound: all traffic (agents and Trivy downloads)

Step 4 — Create an ALB Security Group

  • Inbound: TCP 443 from your allowed IP range (or your corporate security group)
  • Inbound: TCP 80 from same range (for HTTP→HTTPS redirect)
  • Outbound: TCP 3443 to the EC2 instance security group

Step 5 — Create a Target Group

  • Target type: Instance
  • Protocol: HTTP
  • Port: 3443
  • Protocol version: HTTP1 (not HTTP2)
  • Health check path: /healthz
  • Register your EC2 instance as a target

Step 6 — Create the Application Load Balancer

  • Scheme: Internet-facing (or Internal if only accessed via VPN)
  • Subnets: select public subnets (subnets with an Internet Gateway route) for internet-facing
  • Security group: the ALB security group from Step 4

Step 7 — Add ALB Listeners

  • HTTPS:443 → Forward to your target group. Attach your ACM certificate here.
  • HTTP:80 → Redirect to HTTPS:443 (status 301). Do not forward to the target group.

Step 8 — Create a DNS record

In Route 53 (or your DNS provider), create an A record (Alias) for your domain pointing to the ALB DNS name. If using a third-party DNS provider, create a CNAME record instead.

Step 9 — Verify

# From the EC2 instance — should return {“status”:”ok”}
curl http://localhost:3443/healthz

# From your browser — should load the login page with a trusted certificate
https://your-domain.com

 

Option C — Custom / Wildcard Certificate (Direct TLS on the Container)

Best for: deployments where you have your own certificate (Sectigo, DigiCert, Let’s Encrypt, wildcard) and want the container to terminate TLS directly without a load balancer.

Step 1 — Prepare certificate files

You need two files:

  • server.crt — your certificate plus the full chain (intermediate and root CAs concatenated in order)
  • server.key — the private key

For a Sectigo / commercial wildcard certificate, concatenate in this order:

cat your-domain.crt intermediate.crt root.crt > server.crt

Step 2 — Upload certificate files to the host

sudo mkdir -p /opt/contigov/certs
sudo cp server.crt /opt/contigov/certs/server.crt
sudo cp server.key /opt/contigov/certs/server.key
sudo chmod 600 /opt/contigov/certs/server.key

Step 3 — Set TLS variables in .env

TLS_MODE=direct
TLS_CERT=/certs/server.crt
TLS_KEY=/certs/server.key
NODE_ENV=production
ALLOWED_ORIGINS=https://your-domain.com
PUBLIC_SERVER_URL=https://your-domain.com

Step 4 — Start the container with the cert volume mounted

docker run -d \
–name contigov-server \
–restart unless-stopped \
-p 443:3443 \
-p 80:3080 \
-v contigov-data:/app/data \
-v /opt/contigov/certs:/certs:ro \
–env-file .env \
reg.lami.co.il/contigov:latest

Step 5 — Verify

curl -v https://your-domain.com/healthz
# Certificate subject should match your domain
# Response: {“status”:”ok”}
 

6. First Login & Initial Setup

Default credentials

Field Value
Username admin
Password admin

Change the password immediately after first login: Settings → Users → Change My Password.

Recommended first-login checklist

  1. Change the admin password — Settings → Users → Change My Password
  2. Verify the server version — shown in the top-left corner of the dashboard
  3. Set your CORS origin — Settings → Security → Allowed Origins (if not already set in .env)
  4. Enable MFA — Settings → Users → Two-Factor Authentication for the admin account
  5. Set the Public Server URL — Settings → General (required for agent config downloads to work correctly)
  6. Download the agent config — Settings → Agent Deploy → Download agent-config.json
  7. Deploy at least one agent — see Section 7 below
 

7. Deploying Agents

Download the agent

  1. Open the web UI → Settings → Agent Deploy
  2. Click Download agent-config.json — pre-filled with your server URL and API key
  3. Download the binary for your target operating system:
File Platform
agent-windows-amd64.exe Windows 64-bit
agent-linux-amd64 Linux x86-64
agent-darwin-amd64 macOS Intel
agent-darwin-arm64 macOS Apple Silicon (M1/M2/M3/M4)

Place agent-config.json in the same folder as the binary. The agent picks it up automatically — no environment variables needed.

 

Windows — Run as a Service (Manual)

The agent self-elevates via UAC when run interactively, or can be installed as a Windows Service that starts automatically at boot with no user interaction.

Interactive (one-time / testing)

.\agent-windows-amd64.exe

A UAC prompt will appear once to allow hosts file modifications.

Windows Service (recommended for production)

Open PowerShell as Administrator:

# Copy files to a permanent location
New-Item -ItemType Directory -Force -Path C:\ProgramData\contigov
Copy-Item agent-windows-amd64.exe C:\ProgramData\contigov\agent.exe
Copy-Item agent-config.json C:\ProgramData\contigov\agent-config.json

# Install and start the service
C:\ProgramData\contigov\agent.exe –install-service
sc start ContigovAgent

The service runs as LocalSystem, survives reboots, and requires no user to be logged in.

Remove the service

sc stop ContigovAgent
C:\ProgramData\contigov\agent.exe –uninstall-service
 

Windows — Mass Deployment via Microsoft Intune

Use this method to deploy the Contigov agent silently to all Windows devices managed by Microsoft Intune (Endpoint Manager) — no user interaction or manual steps on each device.

What you need before starting

  • Microsoft Intune license (Microsoft 365 Business Premium, E3, or E5)
  • The agent-windows-amd64.exe binary
  • The agent-config.json file downloaded from Settings → Agent Deploy
  • The Microsoft Win32 Content Prep Tool (IntuneWinAppUtil.exe) — download free from GitHub

Step 1 — Prepare the installer folder

On your admin workstation, create a folder with the following files:

C:\ContigovPackage\
agent.exe ← renamed from agent-windows-amd64.exe
agent-config.json ← downloaded from Settings → Agent Deploy
install.ps1 ← PowerShell install script (see below)
uninstall.ps1 ← PowerShell uninstall script (see below)

Create install.ps1:

# install.ps1 — runs silently on each managed device
$dest = “C:\ProgramData\contigov”
New-Item -ItemType Directory -Force -Path $dest | Out-Null

Copy-Item “$PSScriptRoot\agent.exe” “$dest\agent.exe” -Force
Copy-Item “$PSScriptRoot\agent-config.json” “$dest\agent-config.json” -Force

# Install and start as a Windows Service
Start-Process -FilePath “$dest\agent.exe” -ArgumentList “–install-service” -Wait -Verb RunAs
Start-Service -Name ContigovAgent

Create uninstall.ps1:

# uninstall.ps1
Stop-Service -Name ContigovAgent -ErrorAction SilentlyContinue
Start-Process -FilePath “C:\ProgramData\contigov\agent.exe” -ArgumentList “–uninstall-service” -Wait
Remove-Item -Path “C:\ProgramData\contigov” -Recurse -Force -ErrorAction SilentlyContinue

Step 2 — Package as an .intunewin file

Open Command Prompt on your admin workstation and run:

IntuneWinAppUtil.exe ^
-c C:\ContigovPackage ^
-s install.ps1 ^
-o C:\ContigovOutput

This produces C:\ContigovOutput\install.intunewin — the file you upload to Intune.

Step 3 — Add the app in Intune

  1. Open intune.microsoft.com and sign in as an Intune Administrator
  2. Navigate to Apps → Windows → Add
  3. Select app type: Windows app (Win32) → Click Select
  4. Under App information, click Select app package file and upload install.intunewin
  5. Fill in the app details:
    • Name: Contigov Agent
    • Description: Docker governance and container policy enforcement agent
    • Publisher: Contigov
    • Version: (paste the version shown in your UI, e.g. 1.0.42)
  6. Click Next

Step 4 — Configure install and uninstall commands

Field Value
Install command powershell.exe -ExecutionPolicy Bypass -File install.ps1
Uninstall command powershell.exe -ExecutionPolicy Bypass -File uninstall.ps1
Install behavior System (runs as SYSTEM — no UAC prompt)
Device restart behavior No specific action

Click Next.

Step 5 — Set detection rules

Detection rules tell Intune whether the app is already installed. Choose Manually configure detection rules and add:

Rule type Field Value
File Path C:\ProgramData\contigov
File File or folder agent.exe
File Detection method File or folder exists

Click Next.

Step 6 — Assign to devices

  1. Under Assignments, click Add group under Required
  2. Select the Azure AD group that contains your target Windows devices (or select All Devices to deploy to all enrolled Windows machines)
  3. Click Next → Create

Step 7 — Monitor deployment

  1. Go to Apps → Windows → Contigov Agent
  2. Click Device install status to see per-device results
  3. Intune deploys silently within the next sync cycle (typically 15–60 minutes). You can force a sync from the device: Settings → Accounts → Access work or school → Info → Sync

Once deployed, the agent appears as a service named ContigovAgent in services.msc and the endpoint shows up in the Contigov dashboard within one report cycle (default 30 seconds after first start).

 

Linux — systemd Service

Step 1 — Copy files

sudo mkdir -p /opt/contigov
sudo cp agent-linux-amd64 /opt/contigov/agent
sudo cp agent-config.json /opt/contigov/agent-config.json
sudo chmod +x /opt/contigov/agent

Step 2 — Create the systemd unit file

Create /etc/systemd/system/contigov-agent.service:

[Unit]
Description=Contigov Agent
After=network.target docker.service

[Service]
ExecStart=/opt/contigov/agent
WorkingDirectory=/opt/contigov
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Step 3 — Enable and start

sudo systemctl daemon-reload
sudo systemctl enable –now contigov-agent

# Check status
sudo systemctl status contigov-agent

# View live logs
journalctl -u contigov-agent -f

Stop / remove

sudo systemctl disable –now contigov-agent
sudo rm /etc/systemd/system/contigov-agent.service
 

macOS — launchd Daemon (Manual)

The launchd daemon runs as root at boot, before any user logs in — no user interaction required.

Step 1 — Copy files

sudo mkdir -p /opt/contigov
sudo cp agent-darwin-arm64 /opt/contigov/agent # or agent-darwin-amd64 for Intel
sudo cp agent-config.json /opt/contigov/agent-config.json
sudo chmod +x /opt/contigov/agent

Step 2 — Remove Gatekeeper quarantine

sudo xattr -rd com.apple.quarantine /opt/contigov/agent

Step 3 — Create the launchd plist

Create /Library/LaunchDaemons/com.contigov.agent.plist:

<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN”
“http://www.apple.com/DTDs/PropertyList-1.0.dtd”>
<plist version=”1.0″>
<dict>
<key>Label</key> <string>com.contigov.agent</string>
<key>ProgramArguments</key>
<array><string>/opt/contigov/agent</string></array>
<key>WorkingDirectory</key> <string>/opt/contigov</string>
<key>RunAtLoad</key> <true/>
<key>KeepAlive</key> <true/>
<key>StandardOutPath</key> <string>/var/log/contigov-agent.log</string>
<key>StandardErrorPath</key> <string>/var/log/contigov-agent.log</string>
</dict>
</plist>

Step 4 — Load and verify

sudo launchctl load -w /Library/LaunchDaemons/com.contigov.agent.plist

# Verify it is running (should show a PID)
sudo launchctl list | grep contigov

# View live logs
tail -f /var/log/contigov-agent.log

Stop / remove

sudo launchctl unload /Library/LaunchDaemons/com.contigov.agent.plist
sudo rm /Library/LaunchDaemons/com.contigov.agent.plist
 

macOS — Mass Deployment via Jamf Pro

Use this method to silently deploy the Contigov agent to all Mac devices enrolled in Jamf Pro — zero touch from the end user.

What you need before starting

  • Jamf Pro instance with admin access
  • The agent-darwin-arm64 and/or agent-darwin-amd64 binaries
  • The agent-config.json downloaded from Settings → Agent Deploy
  • Packages (pkg) tool — use Packages.app (free) or pkgbuild (built into macOS) to create a .pkg installer

Step 1 — Create the install script

Create a file named postinstall (no extension). This runs automatically after the package is installed:

#!/bin/bash
# postinstall — runs as root on each managed Mac

# Remove Gatekeeper quarantine (binary is not notarized)
xattr -rd com.apple.quarantine /opt/contigov/agent 2>/dev/null || true

# Set correct permissions
chmod +x /opt/contigov/agent
chmod 600 /opt/contigov/agent-config.json

# Remove any existing launchd service before loading fresh
launchctl unload /Library/LaunchDaemons/com.contigov.agent.plist 2>/dev/null || true

# Load and enable the daemon
launchctl load -w /Library/LaunchDaemons/com.contigov.agent.plist

exit 0

Make it executable:

chmod +x postinstall

Step 2 — Build the .pkg installer

Arrange files in a staging folder that mirrors the target filesystem:

pkg-root/
opt/
contigov/
agent ← binary (arm64, amd64, or universal)
agent-config.json ← your pre-filled config
Library/
LaunchDaemons/
com.contigov.agent.plist ← the plist from Step 3 above

Build the package using pkgbuild (run from your Mac terminal):

pkgbuild \
–root ./pkg-root \
–scripts ./scripts \
–identifier com.contigov.agent \
–version 1.0.0 \
–install-location / \
ContigovAgent.pkg

The --scripts folder should contain your postinstall file.

Step 3 — Upload the package to Jamf Pro

  1. Log in to your Jamf Pro instance
  2. Go to Settings → Computer Management → Packages
  3. Click New and upload ContigovAgent.pkg
  4. Set the display name to Contigov Agent and click Save

Step 4 — Create an Uninstall Script (optional but recommended)

  1. Go to Settings → Computer Management → Scripts
  2. Click New, name it Uninstall Contigov Agent, set Priority to After
  3. Paste the following into the Script field:
#!/bin/bash
launchctl unload /Library/LaunchDaemons/com.contigov.agent.plist 2>/dev/null
rm -f /Library/LaunchDaemons/com.contigov.agent.plist
rm -rf /opt/contigov
exit 0
  1. Click Save

Step 5 — Create a Jamf Policy

  1. Go to Computers → Policies → New
  2. Under General:
    • Display Name: Deploy Contigov Agent
    • Trigger: Enrollment Complete, Recurring Check-in (or both)
    • Execution Frequency: Once per computer
  3. Under Packages, click Configure → Add ContigovAgent.pkg → Action: Install
  4. Under Scope, choose your target computers or smart group (e.g. All Managed Clients, or a specific department group)
  5. Click Save

Step 6 — Force an immediate check-in (optional)

To deploy without waiting for the next scheduled check-in, use Jamf Remote or run from a managed device:

sudo jamf policy -trigger enrollmentComplete

Step 7 — Verify deployment

In Jamf Pro, go to Computers → Policies → Deploy Contigov Agent → Logs to see per-device install results.

On any deployed Mac, verify the agent is running:

sudo launchctl list | grep contigov
# Expected output: a line with a PID and com.contigov.agent

tail -20 /var/log/contigov-agent.log
# Expected: agent startup messages and first report to the server

The device should appear in the Contigov dashboard within 30 seconds of the agent starting for the first time.

Updating the agent via Jamf

  1. Download the new binary from Settings → Agent Deploy (the agent-config.json does not change unless your server URL or API key changes)
  2. Build a new .pkg with an incremented version number
  3. Upload the new package to Jamf and update the policy to reference it
  4. Change the policy Execution Frequency to Once per computer per version so it reinstalls only on devices that have an older version
 

8. Health & Readiness Checks

Two HTTP endpoints are available for monitoring and load balancer health checks:

Endpoint Auth Returns Use for
GET /healthz None {"status":"ok"} Docker HEALTHCHECK, ALB health check, uptime monitors
GET /readyz None {"status":"ready"} or 503 Kubernetes readiness probe, deployment gates — verifies DB connectivity
# Quick check from the host
curl https://your-domain.com/healthz
# Expected: {“status”:”ok”}

curl https://your-domain.com/readyz
# Expected: {“status”:”ready”}

 

9. Database Backups

The server can create automatic hourly encrypted backups of the database. Backups are AES-256-GCM encrypted (using the same DB_ENCRYPTION_KEY) and automatically pruned after 7 days.

Enable backups

Add BACKUP_DIR=/backups to your .env file and mount a dedicated volume:

docker volume create contigov-backups

docker run -d \
–name contigov-server \
–restart unless-stopped \
-p 443:3443 \
-p 80:3080 \
-v contigov-data:/app/data \
-v contigov-backups:/backups \
–env-file .env \
reg.lami.co.il/contigov:latest

Restore from backup

# Stop the container
docker stop contigov-server

# Decrypt a backup file
openssl enc -d -aes-256-gcm \
-K $DB_ENCRYPTION_KEY \
-in /path/to/backup.enc \
-out governance.db

# Replace the active database
docker cp governance.db contigov-server:/app/data/governance.db

# Restart
docker start contigov-server

 

10. Upgrading

Upgrades are non-destructive. The database schema is migrated automatically on startup. Always back up your data volume before upgrading.

Pull the new image and restart

# Pull the latest image
docker pull reg.lami.co.il/contigov:latest

# Stop and remove the old container (data volume is preserved)
docker stop contigov-server
docker rm contigov-server

# Start a new container with the same flags as before
docker run -d \
–name contigov-server \
–restart unless-stopped \
-p 443:3443 \
-p 80:3080 \
-v contigov-data:/app/data \
–env-file .env \
reg.lami.co.il/contigov:latest

# Verify the new version
docker logs contigov-server | head -5

After upgrading the server, download a fresh agent-config.json from Settings → Agent Deploy and redeploy the agent binary to all endpoints to ensure version alignment.

 

11. Troubleshooting

Container exits immediately on startup

Check the logs for a fatal startup error:

docker logs contigov-server

Common causes:

  • JWT_SECRET or DB_ENCRYPTION_KEY not set and NODE_ENV=production is active — generate and add the secrets to .env
  • TLS_MODE=direct but cert files are not mounted or paths are wrong — verify TLS_CERT and TLS_KEY paths and the volume mount

Browser shows “Not Secure” or cert warning

  • Self-signed cert: expected — click Advanced → Proceed. For production, use Option B (ALB+ACM) or Option C (custom cert).
  • Wrong certificate loaded: verify with curl -v https://your-domain.com/healthz and check the certificate subject in the output
  • Chrome HSTS cache: visit chrome://net-internals/#hsts, search for your domain, and click Delete

ALB Target Group shows Unhealthy

  • Confirm the EC2 security group allows inbound TCP 3443 from the ALB security group (not from 0.0.0.0/0)
  • Confirm the Target Group protocol version is set to HTTP1 (not HTTP2)
  • Confirm the container is running: docker ps and curl http://localhost:3443/healthz from the EC2 instance
  • Confirm TLS_MODE=proxy is set in .env — the ALB sends plain HTTP to port 3443, not HTTPS

Agents not appearing in the dashboard

  • Verify the agent can reach the server: curl https://your-domain.com/healthz from the endpoint
  • Verify agent-config.json contains the correct server URL and API key
  • Check the agent log for authentication errors (401) — the API key in the agent config must exactly match API_KEY in the server’s .env
  • On macOS, ensure the Gatekeeper quarantine flag was removed: sudo xattr -rd com.apple.quarantine /opt/contigov/agent

.env file not being parsed correctly on Linux

If you created the .env file on Windows and transferred it to Linux, it may contain Windows line endings (\r\n) or a UTF-8 BOM that breaks Docker’s env file parser:

# Strip Windows line endings
sed -i ‘s/\r//’ .env

# Strip UTF-8 BOM (if present)
sed -i ‘1s/^\xEF\xBB\xBF//’ .env

View live server logs

docker logs -f contigov-server

Check server version inside the container

docker exec contigov-server printenv SERVER_VERSION