Configure HTTPS
This guide explains how to expose a self-hosted Synaccess CMP deployment over HTTPS when running with Docker Compose.
For production deployments, Synaccess recommends terminating HTTPS at a reverse proxy or load balancer, then forwarding HTTP traffic to the CMP container on an internal network or localhost-only port. Direct TLS inside the CMP container is also supported when a proxy or load balancer is not available.
Before You Start
You need:
- A DNS name for the CMP portal, such as
cmp.example.com. - A server or load balancer reachable by that DNS name.
- Firewall or security group rules that allow inbound TCP
80and443from the clients and PDUs that need access. - Docker Compose deployment files for CMP.
- A certificate from a trusted certificate authority, or a private CA certificate that all browsers and PDUs trust.
Do not confuse CMP HTTPS with database TLS. The
DB_SSL_*variables only secure the PostgreSQL/TimescaleDB connection. Browser and PDU HTTPS is controlled byHOST,HOST_URL,PORT,CONNECTION_SECURITY,TLS_CERT, andTLS_KEY.
Choose a Deployment Mode
Use one of these modes:
| Mode | Recommended for | CMP container settings |
|---|---|---|
| Reverse proxy or load balancer | Production deployments | CONNECTION_SECURITY=none, PORT=80, no TLS_CERT or TLS_KEY |
| Direct TLS inside CMP | Simple single-host deployments without a proxy | CONNECTION_SECURITY=tls, PORT=443, TLS_CERT and TLS_KEY set |
Do not enable both modes at the same time. If nginx, Apache, a cloud load balancer, or a firewall appliance is terminating TLS, keep CMP itself on HTTP internally.
Option 1: HTTPS with nginx Reverse Proxy
Use this option when nginx runs on the same host as the CMP container.
1. Configure CMP for Internal HTTP
Set the CMP .env values like this:
HOST=cmp.example.com
PORT=80
BIND_HOST=0.0.0.0
HOST_URL=https://cmp.example.com
CONNECTION_SECURITY=none
FORWARD_PORT_80_TO_443=false
TLS_CERT=
TLS_KEY=Replace cmp.example.com with your real DNS name.
2. Publish CMP on a Localhost-Only Port
If nginx is on the same host, nginx must own public ports 80 and 443. Edit the CMP docker-compose.yml so the container is reachable only from the host:
services:
syn-cloud:
ports:
- "127.0.0.1:3000:80"This maps http://127.0.0.1:3000 on the host to port 80 inside the CMP container. It also prevents direct public access to the container over plain HTTP.
Start or restart CMP:
docker compose up -dConfirm the container is reachable locally:
curl -I http://127.0.0.1:30003. Install nginx and Certbot
On Ubuntu/Debian systems:
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginxConfirm DNS points to the server before requesting a certificate:
dig +short cmp.example.com4. Create the nginx Site
Create /etc/nginx/sites-available/cmp.example.com:
server {
listen 80;
server_name cmp.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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 $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}Enable the site and reload nginx:
sudo ln -s /etc/nginx/sites-available/cmp.example.com /etc/nginx/sites-enabled/cmp.example.com
sudo nginx -t
sudo systemctl reload nginx5. Request and Install the HTTPS Certificate
Run Certbot:
sudo certbot --nginx -d cmp.example.comWhen prompted, choose the option to redirect HTTP traffic to HTTPS.
Certbot updates the nginx site with certificate paths similar to:
ssl_certificate /etc/letsencrypt/live/cmp.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cmp.example.com/privkey.pem;6. Validate HTTPS
Open:
https://cmp.example.comThen verify from the server:
curl -I https://cmp.example.com
sudo certbot renew --dry-runIf the page loads but generated links or emails still use http://, confirm HOST_URL=https://cmp.example.com is set in .env and restart CMP:
docker compose restart syn-cloudOption 2: HTTPS with an External Load Balancer
Use this option when HTTPS is terminated by AWS ALB, Azure Application Gateway, F5, HAProxy, nginx on a separate server, or another network appliance.
Set CMP to HTTP internally:
HOST=cmp.example.com
PORT=80
HOST_URL=https://cmp.example.com
CONNECTION_SECURITY=none
FORWARD_PORT_80_TO_443=false
TLS_CERT=
TLS_KEY=Configure the load balancer or proxy to:
- Listen publicly on
443. - Use a valid certificate for
cmp.example.com. - Redirect public
80traffic tohttps://cmp.example.com. - Forward traffic to the CMP container or host over HTTP.
- Preserve the original host header.
- Send
X-Forwarded-Proto: https. - Send
X-Forwarded-Forwith the client IP chain.
The CMP container does not need access to the TLS private key in this mode.
Option 3: Direct TLS Inside the CMP Container
Use this option only when you are not using a reverse proxy or load balancer.
Example Certbot Commands
Use Certbot only when Let's Encrypt can validate the DNS name or public IP address. For private lab networks or fully air-gapped environments, use the self-signed or private CA approach in Lab or Air-Gapped Sites Using an IP Address.
Public DNS Name with Standalone Validation
The standalone Certbot plugin starts a temporary validation web server and must bind to port 80. If CMP is already using port 80, stop CMP before requesting or renewing the certificate.
Replace cmp.example.com and [email protected] with your hostname and notification email:
sudo apt update
sudo apt install -y certbot
docker compose stop syn-cloud
sudo certbot certonly --standalone \
--agree-tos \
--no-eff-email \
--email [email protected] \
-d cmp.example.com
mkdir -p certs
sudo install -m 644 /etc/letsencrypt/live/cmp.example.com/fullchain.pem certs/fullchain.pem
sudo install -m 600 /etc/letsencrypt/live/cmp.example.com/privkey.pem certs/privkey.pem
docker compose up -dIf the container runs as a non-root user, ensure it can read certs/privkey.pem.
DNS Challenge When Port 80 Cannot Be Opened
If inbound port 80 is blocked but the site has public DNS, use a DNS challenge:
sudo certbot certonly --manual \
--preferred-challenges dns \
--agree-tos \
--no-eff-email \
--email [email protected] \
-d cmp.example.comCertbot will prompt you to create a TXT record under _acme-challenge.cmp.example.com. Manual DNS challenges do not renew automatically unless you also configure an authentication hook or a DNS provider plugin.
After Certbot issues the certificate, copy the files into the CMP certs/ directory:
mkdir -p certs
sudo install -m 644 /etc/letsencrypt/live/cmp.example.com/fullchain.pem certs/fullchain.pem
sudo install -m 600 /etc/letsencrypt/live/cmp.example.com/privkey.pem certs/privkey.pem
docker compose restart syn-cloudPublic IP Address Certificate
Certbot can request Let's Encrypt certificates for public IP addresses in current Certbot versions. Use Certbot 5.4 or newer. These certificates use Let's Encrypt's short-lived profile and are valid for about six days, so automatic renewal is important.
Replace 203.0.113.10 with the public IP address:
docker compose stop syn-cloud
sudo certbot certonly --standalone \
--preferred-profile shortlived \
--agree-tos \
--no-eff-email \
--email [email protected] \
--ip-address 203.0.113.10
mkdir -p certs
sudo install -m 644 /etc/letsencrypt/live/203.0.113.10/fullchain.pem certs/fullchain.pem
sudo install -m 600 /etc/letsencrypt/live/203.0.113.10/privkey.pem certs/privkey.pem
docker compose up -dThis does not apply to private RFC1918 addresses such as 192.168.x.x, 10.x.x.x, or 172.16-31.x.x in an isolated network. Use a private CA or self-signed certificate for those environments.
1. Place Certificates Next to Docker Compose
Create a certs/ directory next to docker-compose.yml:
mkdir -p certsCopy the certificate and key:
certs/fullchain.pem
certs/privkey.pemUse the full certificate chain, not only the leaf certificate. For Let's Encrypt, fullchain.pem and privkey.pem are the expected files.
2. Mount Certificates and Publish HTTPS
Update docker-compose.yml:
services:
syn-cloud:
volumes:
- ./certs:/certs:ro
ports:
- "80:80"
- "443:443"Publishing 80:80 is only required when FORWARD_PORT_80_TO_443=true. If you do not want HTTP-to-HTTPS redirect from the container, publish only 443:443.
3. Configure .env
.envSet:
HOST=cmp.example.com
PORT=443
BIND_HOST=0.0.0.0
HOST_URL=https://cmp.example.com
CONNECTION_SECURITY=tls
FORWARD_PORT_80_TO_443=true
TLS_CERT=/certs/fullchain.pem
TLS_KEY=/certs/privkey.pemStart or restart CMP:
docker compose up -d
docker compose logs -f syn-cloudThe logs should show that CMP is listening with HTTPS/TLS.
4. Renew Certificates
When the certificate is renewed on the host, copy the renewed fullchain.pem and privkey.pem into the certs/ directory and restart CMP:
docker compose restart syn-cloudCMP reads the certificate and key at startup.
For standalone Certbot renewals, port 80 must be free during validation. Replace /opt/syn-cloud with the directory that contains your docker-compose.yml, then create a deploy hook:
sudo tee /usr/local/sbin/syn-cloud-deploy-cert.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
CMP_DIR="/opt/syn-cloud"
install -m 644 "$RENEWED_LINEAGE/fullchain.pem" "$CMP_DIR/certs/fullchain.pem"
install -m 600 "$RENEWED_LINEAGE/privkey.pem" "$CMP_DIR/certs/privkey.pem"
docker compose --project-directory "$CMP_DIR" -f "$CMP_DIR/docker-compose.yml" restart syn-cloud
EOF
sudo chmod +x /usr/local/sbin/syn-cloud-deploy-cert.shTest renewal with pre/post hooks that stop CMP before validation and start it again afterward:
sudo certbot renew --dry-run \
--pre-hook "docker compose --project-directory /opt/syn-cloud -f /opt/syn-cloud/docker-compose.yml stop syn-cloud" \
--deploy-hook /usr/local/sbin/syn-cloud-deploy-cert.sh \
--post-hook "docker compose --project-directory /opt/syn-cloud -f /opt/syn-cloud/docker-compose.yml up -d"If the dry run succeeds, make those hooks part of scheduled renewal. Configure your Certbot service or cron entry with the same --pre-hook, --deploy-hook, and --post-hook values, or place equivalent executable scripts under Certbot's renewal hook directories:
/etc/letsencrypt/renewal-hooks/pre/
/etc/letsencrypt/renewal-hooks/deploy/
/etc/letsencrypt/renewal-hooks/post/Lab or Air-Gapped Sites Using an IP Address
Some lab, isolated, or air-gapped deployments do not have internal DNS. In those environments, CMP can use HTTPS on a static IP address with a self-signed certificate or a certificate issued by your organization's private CA.
The certificate must include the IP address in the Subject Alternative Name (SAN). Modern browsers and clients do not rely on the Common Name alone.
Create a Self-Signed Certificate for an IP Address
Replace 192.168.1.50 with the static IP address users and PDUs will use to reach CMP:
mkdir -p certs
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout certs/privkey.pem \
-out certs/fullchain.pem \
-days 365 \
-subj "/CN=192.168.1.50" \
-addext "subjectAltName = IP:192.168.1.50"Then configure CMP for direct TLS:
HOST=192.168.1.50
PORT=443
BIND_HOST=0.0.0.0
HOST_URL=https://192.168.1.50
CONNECTION_SECURITY=tls
FORWARD_PORT_80_TO_443=true
TLS_CERT=/certs/fullchain.pem
TLS_KEY=/certs/privkey.pemMount the certificates and publish HTTPS in docker-compose.yml:
services:
syn-cloud:
volumes:
- ./certs:/certs:ro
ports:
- "80:80"
- "443:443"Restart CMP:
docker compose up -dAccess the portal at:
https://192.168.1.50Trust Requirements
Self-signed certificates are encrypted, but they are not trusted automatically.
- Browsers will show a certificate warning until the certificate, or the CA that issued it, is trusted by the workstation.
- PDUs must trust the certificate chain before certificate validation will succeed. For a self-signed certificate, upload the self-signed certificate as the trusted CA if the PDU supports that workflow.
- If the CMP IP address changes, issue a new certificate with the new IP address in the SAN.
For larger air-gapped environments, use an internal CA instead of one self-signed certificate per server. Issue the CMP certificate from that CA, include the CMP IP address in the SAN, and distribute the CA certificate to administrator workstations and PDUs.
PDU Certificate Validation
If PDUs connect to CMP using https://cmp.example.com or https://192.168.1.50, the certificate must be trusted by the PDU.
- Public CA certificate: no extra PDU configuration is usually required.
- Private CA or self-signed certificate: upload the CA certificate bundle to each PDU before enabling certificate validation.
- Hostname or IP validation: the Server URL configured on the PDU must match the certificate Subject Alternative Name. For example,
https://cmp.example.comrequires a DNS SAN forcmp.example.com, andhttps://192.168.1.50requires an IP SAN for192.168.1.50.
Avoid disabling certificate validation except for short troubleshooting windows.
Troubleshooting
Browser cannot connect
Check DNS and firewall rules:
dig +short cmp.example.com
curl -I http://cmp.example.com
curl -I https://cmp.example.comConfirm public ports 80 and 443 are open to the users and PDUs that need access.
nginx returns 502 Bad Gateway
Confirm CMP is running and reachable from the nginx host:
docker compose ps
curl -I http://127.0.0.1:3000If curl fails, check the container logs:
docker compose logs -f syn-cloudCertificate warning in browser
Common causes:
- The certificate is self-signed and the client does not trust the CA.
- The certificate is missing the intermediate chain. Use
fullchain.pem. - The browser URL does not match the certificate hostname.
- The certificate is expired.
Inspect the certificate:
openssl s_client -connect cmp.example.com:443 -servername cmp.example.com -showcertsCMP logs say TLS certificate could not be loaded
For direct TLS mode, verify:
CONNECTION_SECURITY=tls.TLS_CERT=/certs/fullchain.pem.TLS_KEY=/certs/privkey.pem.docker-compose.ymlmounts./certs:/certs:ro.- The host files exist at
certs/fullchain.pemandcerts/privkey.pem.
Restart after fixing file paths:
docker compose restart syn-cloudHTTPS works but links use HTTP
Set the full public URL explicitly:
HOST_URL=https://cmp.example.comThen restart CMP:
docker compose restart syn-cloudCertbot cannot issue a certificate
Check:
- DNS points to this server.
- Port
80is publicly reachable. - nginx is running.
- No other process is already bound to port
80. - You are requesting the exact hostname users will enter in the browser.
PDU cannot connect over HTTPS
Check:
- The PDU Server URL includes
https://. - The PDU can resolve the CMP DNS name.
- Firewall rules allow the PDU network to reach TCP
443. - The certificate hostname matches the Server URL.
- If using a private CA, the CA bundle has been uploaded to the PDU.
Quick Reference
Reverse Proxy Mode
HOST=cmp.example.com
PORT=80
HOST_URL=https://cmp.example.com
CONNECTION_SECURITY=none
FORWARD_PORT_80_TO_443=false
TLS_CERT=
TLS_KEY=Direct TLS Mode
HOST=cmp.example.com
PORT=443
HOST_URL=https://cmp.example.com
CONNECTION_SECURITY=tls
FORWARD_PORT_80_TO_443=true
TLS_CERT=/certs/fullchain.pem
TLS_KEY=/certs/privkey.pemUpdated 1 day ago