This guide explains how to set up SSL certificates for your VPN-only services using mkcert, a tool for making locally-trusted development certificates.
mkcertinstalled on your local machine- Access to the server where certificates will be deployed
- WireGuard VPN connection configured
Note: Generating new certificates is optional. The repository includes pre-generated certificates in the
certs/directory. You can simply install themkcert-root-ca.crton your client devices to get started immediately. Follow "Step 8: Install CA Certificate" if you choose to use the existing certificates.
On macOS:
brew install mkcertOn Linux:
# Download from https://github.com/FiloSottile/mkcert/releases
# Or use package manager
sudo apt install mkcert # Ubuntu/DebianOn Windows:
choco install mkcert
# Or download from GitHub releasesImportant: This installs a Certificate Authority (CA) on your local machine that will be trusted by your browser.
mkcert -installThis creates a local CA and adds it to your system's trust store. You'll see:
Created a new local CA at "/Users/yourname/Library/Application Support/mkcert"
The local CA is now installed in the system trust store! ⚠️
The local CA is now installed in the Firefox trust store! ⚠️
Navigate to your project directory and create a certs folder:
cd /path/to/myJenkinsSetup
mkdir -p certs
cd certsGenerate certificates for all your services:
mkcert jenkins.hs kanban.hs draw.hs diagram.hs startpage.hs tempmail.hs semaphoreui.hsOutput:
Created a new certificate valid for the following names 📜
- "jenkins.hs"
- "kanban.hs"
- "draw.hs"
- "diagram.hs"
The certificate is at "./jenkins.hs+3.pem" and the key at "./jenkins.hs+3-key.pem" ✅
It will expire on [date] 🗓
Note: The certificate name includes +6 because it covers 6 additional domains (7 total: jenkins.hs + 6 others).
Copy the certificates to your server:
# From your local machine
scp certs/jenkins.hs+3.pem user@your-server:/path/to/app/certs/
scp certs/jenkins.hs+3-key.pem user@your-server:/path/to/app/certs/Or if you're already on the server:
# Create certs directory on server
mkdir -p /root/app/certs
# Copy certificates (adjust path as needed)
# Certificates should be in: /root/app/certs/Ensure docker-compose.vpn.yml has the certificate volume mounted:
nginx:
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro # Certificates directory
ports:
- "10.200.0.1:8980:80"
- "10.200.0.1:8943:443" # HTTPS port (Mapped to 8943, forwarded from 443 via iptables)The nginx.conf should reference the certificates:
server {
listen 443 ssl;
server_name jenkins.hs;
ssl_certificate /etc/nginx/certs/jenkins.hs+3.pem;
ssl_certificate_key /etc/nginx/certs/jenkins.hs+3-key.pem;
# ... rest of config
}On your server:
cd /root/app
docker compose up -d --force-recreate nginxImportant: For browsers to trust the certificates, you need to install the local CA certificate on each device that will access the services.
macOS:
mkcert -CAROOT
# Output: /Users/yourname/Library/Application Support/mkcert
# CA files: rootCA.pem and rootCA-key.pemLinux:
mkcert -CAROOT
# Usually: ~/.local/share/mkcertWindows:
mkcert -CAROOT
# Usually: %LOCALAPPDATA%\mkcert- Copy
rootCA.pemto your device - Double-click the file
- Open Keychain Access
- Find "mkcert" certificate
- Double-click and set "Trust" to "Always Trust"
- Copy
rootCA.pemto your device - Double-click the file
- Click "Install Certificate"
- Choose "Local Machine" → "Place all certificates in the following store"
- Select "Trusted Root Certification Authorities"
- Click "Finish"
# Copy rootCA.pem to device
sudo cp rootCA.pem /usr/local/share/ca-certificates/mkcert.crt
sudo update-ca-certificates- Copy
rootCA.pemto your device - iOS: Settings → General → VPN & Device Management → Install Profile
- Android: Settings → Security → Install from storage → Select rootCA.pem
Once connected to WireGuard VPN and CA certificate installed:
- Jenkins:
https://jenkins.hs - Kanban:
https://kanban.hs - Excalidraw:
https://draw.hs - Draw.io:
https://diagram.hs - Startpage (Dashboard):
https://startpage.hs - TempMail:
https://tempmail.hs - Ansible Semaphore:
https://semaphoreui.hs
Note: HTTP requests automatically redirect to HTTPS.
Symptom: Browser shows "Not Secure" or certificate warning
Solution:
- Ensure CA certificate (
rootCA.pem) is installed on your device - Verify CA is in system trust store:
mkcert -install(if not already done) - Clear browser cache and restart browser
Symptom: Nginx error: SSL_CTX_use_certificate_file() failed
Solution:
# Verify certificates exist on server
ls -la /root/app/certs/
# Check file permissions (should be readable)
chmod 644 /root/app/certs/jenkins.hs+6.pem
chmod 600 /root/app/certs/jenkins.hs+6-key.pem
# Verify docker volume mount
docker compose exec nginx ls -la /etc/nginx/certs/Symptom: Cannot connect to HTTPS
Solution:
# Verify port is bound correctly
sudo ss -tulpn | grep :443
# Check docker-compose ports configuration
# Should have: "10.200.0.1:8943:443"
# Restart nginx
docker compose restart nginxSymptom: Certificate expired error
Solution:
# Check expiration date
openssl x509 -in certs/jenkins.hs+6.pem -noout -dates
# Regenerate certificates
cd certs
mkcert jenkins.hs kanban.hs draw.hs diagram.hs startpage.hs tempmail.hs semaphoreui.hs
# Restart nginx
docker compose restart nginxCertificates generated by mkcert are valid for up to 825 days (approximately 2.25 years). The expiration date is shown when generating:
It will expire on 20 April 2028 🗓
To renew before expiration:
- Regenerate certificates using the same command
- Copy new certificates to server
- Restart nginx:
docker compose restart nginx
- Local CA Only: These certificates are for development/internal use only
- VPN-Only Access: Services are only accessible via WireGuard VPN
- Private Key Security: Keep
*-key.pemfiles secure and never commit them to git - CA Certificate: The
rootCA.pemcan sign any certificate - protect it - Not for Production: Use Let's Encrypt or proper CA certificates for public-facing production services
myJenkinsSetup/
├── certs/
│ ├── jenkins.hs+6.pem # Certificate (public)
│ ├── jenkins.hs+6-key.pem # Private key (keep secure!)
│ └── rootCA.pem # CA certificate (for client installation)
├── nginx.conf # Nginx config with SSL
└── docker-compose.vpn.yml # Docker compose with cert volume mount