Casual Arts · operations runbook

Certificate Renewal Runbook

Renewing the Let's Encrypt certificates on the two EC2 web servers that sit outside CloudFront. The Confluence box is locked to an IP allowlist, so port 80 has to be opened for the renewal and closed straight after.

Status  Current procedure Checked  27 October 2026 Region  us-west-2

What has a cert

overview

The sites moved to CloudFront (casualarts.uk, www, shop, guides, game) use the ACM wildcard, which renews on its own as long as its DNS validation CNAME stays in Route 53. Everything on this page is about the two EC2 boxes that still terminate TLS themselves with certbot and Apache.

ServerCertificatesValidationRenews unattended?
confluencePiwigoCloudServerconf.casualarts.uk, piwigo.casualarts.uk (one cert each, webroot)HTTP-01 on port 80no port 80 is closed by the security group
Reports boxrep.casualarts.uk (cert name is still www.casualarts.uk)HTTP-01 via the Apache pluginyes once the timer is confirmed
Lifetime Let's Encrypt certificates last 90 days and certbot renews once fewer than 30 remain. Because the Confluence box cannot renew by itself, put a reminder in the calendar for about 60 days after each renewal.

Check expiry

from the Mac

Run this from a local terminal, not on the server. It connects like a browser and prints the issuer and validity dates of the cert the site actually presents.

for h in conf piwigo rep; do
  echo "== $h.casualarts.uk"
  echo | openssl s_client -connect $h.casualarts.uk:443 -servername $h.casualarts.uk 2>/dev/null \
    | openssl x509 -noout -issuer -dates
done

An Amazon issuer means the name is behind CloudFront and ACM handles it. A Let's Encrypt issuer means it is one of the boxes below. The Confluence box only accepts 443 from allowlisted addresses, so run the check from one of those.

Confluence box: conf & piwigo

manual, port 80
Why The security group confluencePiwigoCloudServer-private-access only admits SSH and HTTPS from a short allowlist of named addresses, and has no port 80 rule at all. Let's Encrypt validates from addresses we can't predict, so HTTP-01 fails with Timeout during connect (likely firewall problem). The fix is to open TCP 80 to the world only for the renewal, then remove it again. Port 443 stays allowlisted throughout.

1. Confirm the failure (on the box)

sudo certbot renew --dry-run

Expect both conf and piwigo to fail with a connection timeout. A different error (403, 404, wrong IP) is a different problem; see Troubleshooting.

2. Open port 80 temporarily

Console: EC2 → Instances → confluencePiwigoCloudServer → Security tab → the security group → Edit inbound rules → Add rule: type HTTP, port 80, source Anywhere-IPv4 (0.0.0.0/0), description TEMP certbot renewal. Save.

CLI (from the Mac): this keeps the new rule's ID so step 5 removes exactly that rule and nothing else.

export AWS_REGION=us-west-2
SG=$(aws ec2 describe-security-groups \
  --filters Name=group-name,Values=confluencePiwigoCloudServer-private-access \
  --query 'SecurityGroups[0].GroupId' --output text)
echo "$SG"        # should print sg-..., not None

RULE=$(aws ec2 authorize-security-group-ingress --group-id "$SG" \
  --ip-permissions 'IpProtocol=tcp,FromPort=80,ToPort=80,IpRanges=[{CidrIp=0.0.0.0/0,Description="TEMP certbot renewal"}]' \
  --query 'SecurityGroupRules[0].SecurityGroupRuleId' --output text)
echo "$RULE"      # keep this terminal open

If $SG prints None, the name in the console is different. Copy the sg- ID from the instance's Security tab instead.

3. Check it is reachable

curl -I --max-time 10 http://conf.casualarts.uk/
curl -I --max-time 10 http://piwigo.casualarts.uk/

Any HTTP response (200, 301, 404) is fine. A timeout means the rule hasn't applied or there's another firewall in the way. ufw is inactive on this box, and Apache already listens on 80.

4. Renew (on the box)

sudo certbot renew
sudo systemctl reload apache2

If certbot says the certs are not yet due (you're early), use sudo certbot renew --force-renewal, or wait. There's no need to renew more often than every 60 days.

5. Close port 80 again

aws ec2 revoke-security-group-ingress --group-id "$SG" --security-group-rule-ids "$RULE"
curl -I --max-time 10 http://conf.casualarts.uk/   # should now time out

In the console, delete the TEMP certbot renewal rule. Afterwards, check that the inbound rules contain only the allowlist entries.

Don't skip Leaving port 80 open removes the reason for the allowlist. If the session is interrupted, check the security group for the TEMP rule before you walk away.

6. Verify

Re-run the expiry check from an allowlisted machine. Both notAfter dates should be about 90 days out. Set the calendar reminder for the next renewal.

Reports box: rep

automatic

This box's cert originally covered casualarts.uk, www and rep. After the apex and www moved to CloudFront, their challenges reached CloudFront and returned 403, which failed the whole renewal. On 26 October 2026 the existing cert was reissued for rep only. The cert name and file paths were kept, so the Apache config still points at /etc/letsencrypt/live/www.casualarts.uk/:

sudo certbot certonly --apache --cert-name www.casualarts.uk -d rep.casualarts.uk
sudo apachectl configtest && sudo systemctl reload apache2
sudo certbot renew --dry-run

The dead casualarts.uk/www 443 vhost was removed from /etc/apache2/sites-enabled/casualarts.uk.conf. From now on, renewal is just sudo certbot renew --dry-run to confirm, then let the timer do it.

AnsibleThe 443 vhost is Ansible-managed. Remove the casualarts.uk/www vhost from the playbook too, or the next run will restore it. TidyThe cert name www.casualarts.uk is now misleading. It's harmless. To fix it, issue a rep.casualarts.uk cert, repoint the vhost's SSLCertificateFile/KeyFile, then certbot delete --cert-name www.casualarts.uk. BackupsEditor backups (*.conf.save.N) in sites-enabled aren't loaded, but they confuse greps. Move them to /root/apache-backups/.

Renewal timers

both boxes
systemctl list-timers | grep -i certbot
# nothing listed? (snap installs use snap.certbot.renew.timer)
sudo systemctl enable --now certbot.timer

On the Confluence box the timer still runs, but every attempt fails while port 80 is closed. That's expected. The failures show up in /var/log/letsencrypt/letsencrypt.log, which also makes them a useful early warning.

Troubleshooting

symptoms
certbot saysMeaningFix
Timeout during connectNothing reachable on port 80Security group (step 2), then sudo ufw status, then sudo ss -tlnp | grep ':80 ' to confirm Apache is listening.
Invalid response … 403The name resolves somewhere else, usually CloudFront (2600:9000: / Amazon addresses)Drop that name from the cert. CloudFront and ACM already cover it.
Invalid response … 404The challenge path isn't served from the webrootCheck that the port-80 vhost serves /.well-known/acme-challenge/ and isn't redirecting it somewhere broken.
Wrong IP in the errorDNS points elsewheredig +short <name> and fix the Route 53 A record.

Longer term

options

Two ways to stop the Confluence renewal being a manual job every 60 days:

DNS-01Validate through Route 53 instead of port 80. sudo apt install python3-certbot-dns-route53, give the instance role route53:ListHostedZones, route53:GetChange and route53:ChangeResourceRecordSets on the casualarts.uk zone, then reissue with certbot certonly --dns-route53 --cert-name conf.casualarts.uk -d conf.casualarts.uk (and the same for piwigo). The paths stay the same, it needs no inbound ports, and it renews unattended. CloudFrontPut the box behind CloudFront with the ACM wildcard. This fits the Terraform setup, but the IP allowlist would then need to move into a WAF rule.