Automatic Let's Encrypt for production. mkcert for local dev. Custom CA when you need it.
# Local dev — trusted HTTPS in one command
nself trust
# Production — request Let's Encrypt cert
nself ssl request --domain yourdomain.com
# Check cert status
nself ssl status
# Renew now (auto-renewal fires at 30 days)
nself ssl renew
# Verify cert chain
nself ssl verify --domain yourdomain.comnself trust installs mkcert, creates a local CA, and issues a wildcard certificate for *.local.nself.org. Your browser trusts it immediately.
nself trust
# What happens:
# 1. Installs mkcert (Homebrew / apt / choco)
# 2. Creates local CA in ~/Library/Application Support/mkcert/
# 3. Installs CA into macOS Keychain / Firefox / Chrome
# 4. Issues *.local.nself.org wildcard certificate
# 5. Configures Nginx to serve HTTPS on all services
# Access your stack over HTTPS
open https://api.local.nself.org
open https://auth.local.nself.orgNote: nself trust is idempotent — safe to run multiple times. It skips steps that are already configured. On macOS it may prompt for your admin password once to install the CA into the system keychain.
nself trust remove
# Removes the local CA from all trust stores.
# Certificates issued by it will stop working.ɳSelf uses Let's Encrypt via ACME HTTP-01 challenge. Your domain must be publicly reachable on port 80.
# Single domain
nself ssl request --domain yourdomain.com
# Multiple domains / subdomains
nself ssl request --domain yourdomain.com --domain api.yourdomain.com --domain auth.yourdomain.com
# Wildcard (requires DNS-01 challenge — see below)
nself ssl request --domain "*.yourdomain.com" --challenge dnsWildcards require DNS-01 challenge. ɳSelf supports Cloudflare, Route53, and Hetzner DNS automatically:
# Cloudflare (recommended)
# In .env.secrets:
CF_API_TOKEN=<your Cloudflare API token>
nself ssl request --domain "*.yourdomain.com" --challenge dns --dns-provider cloudflare
# Route53
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
nself ssl request --domain "*.yourdomain.com" --challenge dns --dns-provider route53Auto-renewal is enabled by default for all production deployments. It checks daily and renews when expiry is under 30 days.
# Verify auto-renewal is active
nself ssl auto-renew status
# Enable (if disabled)
nself ssl auto-renew --enable
# Test the renewal process (dry run — no cert issued)
nself ssl renew --dry-run
# Force renewal now
nself ssl renew --forcenself ssl status
# Output:
# Domain Issuer Expiry Status
# yourdomain.com Let's Encrypt 2026-07-28 VALID (81 days)
# api.yourdomain.com Let's Encrypt 2026-07-28 VALID (81 days)
# auth.yourdomain.com Let's Encrypt 2026-07-28 VALID (81 days)For internal deployments or enterprise environments that manage their own PKI:
# Install your CA certificate
nself ssl import-ca ./internal-ca.crt
# Import a pre-issued certificate + key
nself ssl import --cert ./server.crt --key ./server.key --domain yourdomain.com
# List imported certificates
nself ssl listɳSelf configures Nginx with secure defaults. The generated config includes:
# What nself prod harden sets in nginx/conf.d/ssl.conf:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# HSTS (2 years)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;Let's Encrypt limits: 50 certificates per domain per week, 5 duplicate certificates per week. Use --staging for testing:
# Request from Let's Encrypt staging (no rate limits, untrusted cert)
nself ssl request --domain yourdomain.com --staging
# Switch to production when ready
nself ssl request --domain yourdomain.com# Check that port 80 is open
nself prod check --item port-80
# If behind a CDN (Cloudflare), temporarily disable the proxy
# (orange cloud → grey cloud) during certificate issuance, then re-enable.# Verify certificate chain
nself ssl verify --domain yourdomain.com
# Check intermediate certificates are served
openssl s_client -connect yourdomain.com:443 -showcerts 2>/dev/null | grep "subject|issuer"
# Reload Nginx after any cert change
nself service reload nginx# Re-install the local CA
nself trust remove
nself trust
# Chrome on macOS: may need to restart Chrome after CA install
# Firefox: uses its own trust store — nself trust handles this automatically.env.secrets, never in the main env file.