Switch to Let's Encrypt
A common lifecycle: you installed with --cert-source self-signed against a bare
IP to get going, and now the box has a real DNS name. This guide migrates the
running install to a Let’s Encrypt certificate issued by certbot — no reinstall,
and your proxy users and instance state survive.
The same steps work if you started from mkcert or a user-supplied
certificate; only the starting certSource differs.
Throughout, substitute your own values for:
norsk.example.com— the DNS name pointing at the boxadmin@example.com— the email Let’s Encrypt sends expiry notices to
Prerequisites
Section titled “Prerequisites”- A DNS A record for the domain pointing at the box’s public IP. Verify it
resolves before touching anything:
Terminal window dig +short norsk.example.com - Inbound ports 80 and 443 open from the internet. Port 443 is already open
if the proxy works today; port 80 is the one commonly missing. On EC2 that
means an inbound security-group rule for TCP 80 from
0.0.0.0/0(and::/0); on other clouds the equivalent firewall rule.
1. Stop the daemon and proxy
Section titled “1. Stop the daemon and proxy”Certbot’s standalone challenge server needs to bind port 80, which the proxy currently holds:
sudo systemctl stop norsk-ctldocker stop norsk-proxy2. Issue the certificate
Section titled “2. Issue the certificate”sudo apt-get install -y certbotsudo certbot certonly --standalone --non-interactive --agree-tos \ -m admin@example.com -d norsk.example.comSuccess looks like Successfully received certificate, with the files under
/etc/letsencrypt/live/norsk.example.com/.
If it fails with Timeout during connect (likely firewall problem), DNS is
resolving (the CA reached out to your IP) but port 80 is blocked — fix the
security-group / firewall rule and re-run. If it fails with
Address already in use, something still holds port 80 — check
sudo ss -tlnp | grep :80.
3. Re-point the config
Section titled “3. Re-point the config”Certificate fields can’t be changed with config set; re-run init with your
current settings plus the certbot trio. Check norsk-ctl config show for your
current networkMode and defaultWorkingDirectory first.
sudo runuser -l norsk -c '/usr/local/bin/norsk-ctl init --force \ --network-mode docker \ --working-directory /var/norsk-ctl \ --cert-source certbot \ --cert-path /etc/letsencrypt/live/norsk.example.com/fullchain.pem \ --key-path /etc/letsencrypt/live/norsk.example.com/privkey.pem \ --public-host norsk.example.com'Confirm it landed in the daemon’s config, not a stray home-dir one:
sudo grep -E "certSource|publicHost" /etc/norsk-ctl/config.yamlProxy users (htpasswd) and the instance database live alongside the daemon and
survive the re-init — you don’t need to re-supply --proxy-user.
4. Restart and verify
Section titled “4. Restart and verify”Always drive the daemon through systemd on server installs — norsk-ctl shutdown from a shell without the production env fails with
Redirected to proxy — stale or missing proxy secret.
sudo systemctl restart norsk-ctlcurl -sI https://norsk.example.com/You should get a response with no certificate warning (no curl -k needed).
If the proxy container instead crash-loops with a key permission error
(docker logs norsk-proxy): /etc/letsencrypt/live and archive are
root-only (0700) by default, and the container may run as a non-root user.
Open them up:
sudo chmod 755 /etc/letsencrypt/live /etc/letsencrypt/archivesudo chmod 640 /etc/letsencrypt/archive/norsk.example.com/privkey1.pem5. Relaunch running instances
Section titled “5. Relaunch running instances”publicHost changed from the IP to the domain, so instances launched before the
switch keep advertising the old address until relaunched. Stop and start each
from the UI or CLI.
The old self-signed pair is now unused:
sudo rm /etc/norsk-ctl/certs/cert.pem /etc/norsk-ctl/certs/key.pem6. Wire up renewal hooks
Section titled “6. Wire up renewal hooks”sudo tee /etc/letsencrypt/renewal-hooks/pre/norsk-proxy-stop.sh >/dev/null <<'EOF'#!/bin/shdocker stop norsk-proxyEOFsudo tee /etc/letsencrypt/renewal-hooks/deploy/norsk-proxy-start.sh >/dev/null <<'EOF'#!/bin/shdocker start norsk-proxyEOFsudo chmod +x /etc/letsencrypt/renewal-hooks/pre/norsk-proxy-stop.sh \ /etc/letsencrypt/renewal-hooks/deploy/norsk-proxy-start.shsudo certbot renew --dry-runA passing dry run proves the whole chain — challenge reachability, hooks, certificate deployment — before the first real renewal at ~day 60. Certbot only runs the hooks when a renewal is actually due, so the proxy stays up day-to-day; the outage is a few seconds every ~60 days.