OneLinersCommand workbench
Guides
Security / Services & Applications

Secure Apache with Let's Encrypt and Certbot

Validate public DNS and HTTP reachability, issue an Apache certificate, enforce HTTPS, test automated renewal, and document revocation and rollback.

45 min8 stepsChanges system stateRevision 2
Save or explore
Save to collectionCreate a collection in the sidebar first.
0 of 8 steps completed
Goal

Deploy renewable public TLS without breaking the existing HTTP virtual host or hiding renewal failures.

Supported environments
  • Ubuntu Server 24.04 LTS
  • Apache HTTP Server 2.4.x
  • Certbot current stable snap
Prerequisites
  • Console and recovery access Keep an independent console session and a copy of the current Apache configuration before changing listeners, modules, or access rules.
  • DNS and network plan Resolve every production hostname to the intended public address and list the exact management and client networks that require access.getent ahosts {{domain}} && ip route show
  • Port ownership Confirm that TCP port 443 is free or already belongs to the intended Apache instance.sudo ss -lntp 'sport = :443'
  • Rollback artifact Snapshot the host or create an archive of the active configuration and web roots before the first material change.
Operating boundary

OneLiners never runs these steps or stores secrets. Review placeholders, versions, current state, and change-control requirements before using a command.

Full guide

What you will build

System
  • A publicly trusted TLS endpoint for an existing Apache name-based virtual host, issued through the ACME protocol only after DNS, HTTP-01 routing, time synchronization, and configuration syntax are proven.
  • A renewable Certbot certificate lineage whose exact names, paths, expiry, timer, Apache edits, redirect behavior, and dry-run result are recorded as operational evidence.
  • A recoverable HTTPS deployment with an archived pre-change Apache and certificate state, explicit distinction between rollback, certificate deletion, and revocation, plus monitoring commands suitable for an incident runbook.
Observable outcome
  • Requests to the selected HTTP hostname redirect to HTTPS, the HTTPS endpoint presents a valid chain for every requested name, and Apache still parses and maps virtual hosts correctly.
  • Certbot's packaged unattended renewal mechanism exists and a staging dry run succeeds without changing the production certificate.
  • Operators know which DNS records, firewall boundaries, challenge path, certificate lineage, private key permissions, renewal logs, and expiry alert must remain healthy.

Architecture

How the parts fit together

The public DNS names resolve to an Apache listener reachable on TCP/80 and TCP/443. Certbot uses the Apache authenticator to answer an ACME HTTP-01 challenge under `/.well-known/acme-challenge/`, then stores a versioned lineage below `/etc/letsencrypt` and adjusts Apache to reference the stable `live` symlinks. Apache terminates TLS and redirects plain HTTP according to the reviewed installer change. A snap-managed timer periodically invokes renewal; Certbot renews only near expiry and runs the installer again when a new certificate is obtained.

Public DNS and HTTP-01 pathProve control of each requested hostname through a publicly routable port-80 response before any certificate can be issued.
Certbot stable snapProvides the ACME client, Apache authenticator/installer, renewal configuration, and packaged update path recommended by the Certbot project.
Let's Encrypt ACME serviceValidates domain control and signs the public certificate; production issuance is rate-limited, while renewal rehearsal uses staging.
Certificate lineageKeeps numbered archive versions and stable live symlinks for certificate, chain, full chain, and private key material.
Apache mod_ssl virtual hostPresents the full chain and private-key proof on TCP/443, selects the certificate by SNI, and performs the approved HTTP redirect.
Renewal timer and monitoringAttempts renewal automatically and exposes logs, expiry data, and dry-run evidence for alerting.
  1. A client resolves the requested name and reaches Apache over port 80; before issuance, a deterministic preflight object proves the same path needed by HTTP-01.
  2. Certbot requests authorization, the Apache plugin temporarily or persistently exposes the challenge response, and the CA validates it from the public Internet.
  3. After authorization, Certbot stores the signed lineage and updates the intended Apache virtual host to reference stable live paths.
  4. Apache parses and reloads the TLS configuration, redirects HTTP, and serves the correct certificate selected by the client's SNI hostname.
  5. The system timer later invokes `certbot renew`; only eligible lineages are renewed, and a dry run exercises the equivalent staging flow in advance.

Assumptions

  • The named Apache HTTP virtual host already serves `/srv/www/{{domain}}/public`, is not an accidental default tenant, and passes `apache2ctl configtest` and `apache2ctl -S`.
  • Every requested A and AAAA record resolves to this service. If an AAAA record exists, IPv6 clients and the CA must reach the same valid challenge endpoint.
  • Inbound TCP/80 and TCP/443 are allowed through the provider edge, NAT, load balancer, and host firewall. Outbound DNS and HTTPS to the ACME service are permitted.
  • The operations email is monitored, system time is synchronized, the hostname is eligible for a public certificate, and issuance rate limits have been reviewed.
  • This guide uses the current stable classic Certbot snap and its Apache integration. Mixing apt, snap, pip, and legacy certbot-auto installations is outside scope.

Key concepts

ACME
The automated protocol through which a client requests domain authorization, certificate issuance, renewal, and revocation from a certificate authority.
HTTP-01
A domain-control challenge retrieved over public TCP/80 at a token-specific path below `/.well-known/acme-challenge/`; it does not validate arbitrary HTTPS behavior.
Lineage
Certbot's managed history and renewal configuration for one certificate name, including stable live symlinks and numbered archived material.
SNI
The TLS client indication that tells a shared Apache listener which hostname and certificate to select before the HTTP request exists.
Full chain
The leaf certificate plus intermediate certificates Apache presents so clients can construct a path to a trusted root.
Renewal dry run
A staging-CA rehearsal of the saved renewal configuration; it validates automation without replacing the live production certificate.
Revocation
A CA-side statement that a certificate should no longer be trusted, normally used for compromise or deliberate invalidation, not as routine rollback.

Before you copy

Values used in this guide

{{domain}}

Primary certificate name and existing Apache ServerName whose public HTTP challenge path is tested.

Example: app.example.com
{{opsEmail}}

Monitored operations address registered with the ACME account for expiry and account notices.

Example: infra@example.com

Security and production boundaries

  • The private key under `/etc/letsencrypt/live` must remain root-controlled and readable only by the service path that needs it. Never print, copy into a ticket, or include it in a web root or general configuration export.
  • A certificate proves control of names at issuance time; it does not make the application secure. Patch Apache and the application, use appropriate headers and cookies, restrict admin paths, and monitor authentication and authorization separately.
  • Do not request names that are absent from the reviewed virtual-host map. A successful installer can still attach policy to the wrong site when ServerName and ServerAlias are ambiguous.
  • Keep port 80 intentionally configured for redirect and HTTP-01 if that authenticator remains in use. Blocking it after issuance can create a renewal incident months later.
  • Treat every installer edit as a privileged configuration change. Review the diff, parse before reload, test every name, and preserve a rollback artifact.
  • Use DNS-01 instead when wildcard names, an unreachable port 80, or central DNS automation make it the correct design; scope DNS credentials to the smallest possible zone operation.

Stop before continuing if

  • Stop before contacting the CA if any requested name resolves elsewhere, an unexpected AAAA record exists, public port 80 fails, or the deterministic challenge object reaches the wrong tenant.
  • Do not install another Certbot distribution over an existing apt, snap, pip, or legacy installation until ownership, renewal jobs, account data, and rollback are understood.
  • Do not accept an Apache installer diff that changes unrelated virtual hosts, weakens access policy, exposes a private key, or references an unexpected lineage.
  • Stop after any configtest, SNI, chain, hostname, redirect, timer, or dry-run failure; keep the known-good HTTP site available while diagnosing.
  • Do not revoke or delete a lineage merely to undo Apache edits. Revocation is deliberate trust invalidation, while rollback restores service configuration.
01

verification

Prove public prerequisites

read-only

Confirm A/AAAA records, the intended challenge path, port 80 reachability, the active Apache host, system time, and a working config before contacting the CA.

Why this step matters

ACME production attempts are rate-limited, so DNS, time, public routing, vhost selection, and the exact HTTP-01 namespace must be proven with a harmless object before creating an order.

What to understand

Create the token-like preflight object under the established document root with non-secret content and mode inherited from the public path.

Resolve every A and AAAA record and compare them with the intended ingress. Test from an external network because a server-side curl may succeed through split DNS or NAT reflection.

Use the access log to prove which virtual host handled the object; a 200 from the wrong tenant is a security failure, not a pass.

System changes

  • Creates one public, non-secret preflight file below the ACME challenge directory; all other commands are read-only.

Syntax explained

install -d -m 0755
Creates a traversable public challenge directory without granting write access to unprivileged users.
getent ahosts
Uses the host resolver path to display IPv4 and IPv6 answers.
timedatectl show -p NTPSynchronized
Reports whether systemd considers time synchronized.
curl --fail
Returns failure for HTTP error statuses so a missing token cannot look successful.
Command
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

sudo install -d -m 0755 /srv/www/{{domain}}/public/.well-known/acme-challenge && printf 'oneliners-acme-preflight\n' | sudo tee /srv/www/{{domain}}/public/.well-known/acme-challenge/oneliners-preflight >/dev/null && getent ahosts {{domain}} && timedatectl show -p NTPSynchronized && sudo apache2ctl configtest && curl --fail http://{{domain}}/.well-known/acme-challenge/oneliners-preflight
Example output / evidence
NTPSynchronized=yes
Syntax OK
oneliners-acme-preflight

Checkpoint: Checkpoint: preflight

Continue whenEvery requested address reaches the intended Apache vhost, the exact object returns `oneliners-acme-preflight`, time is synchronized, and syntax is valid.

Stop whenAny address, port, hostname, challenge path, clock, configtest, access-log vhost, or external response differs from the reviewed design.

If this step fails

The preflight URL returns the application page or a 404.

Likely causeA rewrite, proxy, document root, alias, or vhost selection intercepts the ACME path.

Safe checks
  • sudo apache2ctl -S
  • curl -v http://app.example.com/.well-known/acme-challenge/oneliners-preflight
  • sudo tail -n 50 /var/log/apache2/app.example.com-access.log

ResolutionCorrect the intended vhost and exempt only the precise challenge path where necessary; repeat the external preflight before issuance.

Security notes

  • Never place credentials or account data in the challenge directory; everything below it should be treated as publicly retrievable.

Alternatives

  • Use DNS-01 with an official narrowly scoped plugin when public HTTP-01 cannot be made reliable.

Stop conditions

  • Any address, port, hostname, challenge path, clock, configtest, access-log vhost, or external response differs from the reviewed design.
02

command

Archive Apache and existing certificate state

caution

Preserve site definitions and any existing lineage metadata before allowing the installer plugin to edit configuration.

Why this step matters

The Apache installer can create or modify site files and the first issue creates account and lineage state, so a root-only pre-change archive is essential for service rollback and forensic comparison.

What to understand

Archive `/etc/apache2` and `/etc/letsencrypt` when present without treating a missing first-install certificate tree as an error.

Verify the archive table of contents, checksum, mode, owner, free space, and restore command before allowing an automated installer to edit configuration.

The archive contains private keys when a lineage already exists. Keep it root-only, encrypted according to backup policy, and outside every served or synchronized public path.

System changes

  • Creates `/root/apache-before-certbot.tgz`, potentially containing sensitive existing TLS material, and a checksum record.

Syntax explained

tar -C /
Stores canonical relative paths suitable for a controlled restore.
2>/dev/null ||
Falls back to an Apache-only archive only when a first-install `/etc/letsencrypt` path is absent.
sha256sum
Records archive integrity evidence; it does not encrypt the contents.
Command
sudo tar -C / -czf /root/apache-before-certbot.tgz etc/apache2 etc/letsencrypt 2>/dev/null || sudo tar -C / -czf /root/apache-before-certbot.tgz etc/apache2 && sudo sha256sum /root/apache-before-certbot.tgz
Example output / evidence
a5d8...  /root/apache-before-certbot.tgz

Checkpoint: Checkpoint: backup

Continue whenA non-empty root-owned archive lists the intended configuration paths and its SHA-256 digest verifies.

Stop whenExisting configuration cannot be archived, disk space is insufficient, the archive is readable by unintended users, or a snapshot restore has not been rehearsed.

If this step fails

tar reports files changing or permission errors.

Likely causeAnother certificate job is active, storage is full, or files are being rotated during capture.

Safe checks
  • ps -ef | grep -E '[c]ertbot|[a]pache2'
  • df -h /root
  • sudo tar -tzf /root/apache-before-certbot.tgz | head

ResolutionWait for the owning maintenance job, provide protected capacity, create a new archive, and verify it before continuing.

Security notes

  • Treat this archive like a private-key backup and limit its retention, access, transfer, and incident exposure.

Alternatives

  • Use a provider or filesystem snapshot only when it captures both Apache and Certbot state consistently and has a verified restore path.

Stop conditions

  • Existing configuration cannot be archived, disk space is insufficient, the archive is readable by unintended users, or a snapshot restore has not been rehearsed.
03

command

Install Certbot's Apache integration

caution

Install the current stable classic snap recommended by the Certbot project, expose its command at a predictable path, and record the exact version used to create the lineage.

Why this step matters

One project-supported installation method avoids competing binaries, renewal jobs, plugins, and account directories that make future renewal behavior ambiguous.

What to understand

Inventory `command -v certbot`, package ownership, snap state, and timers first. Remove or migrate an older method under its own change plan rather than overlaying it.

The classic snap is intentionally not strictly sandboxed because the Apache plugin must read and edit system configuration. That privilege makes provenance and path verification important.

Create the `/usr/local/bin` symlink only when absent and verify its resolved target; never overwrite an unknown administrative wrapper.

System changes

  • Installs the current Certbot snap, its refresh lifecycle, and optionally one verified command symlink.

Syntax explained

snap install --classic
Installs the project-recommended snap with host-level access required by installer plugins.
test ! -e
Prevents replacement of an existing command path.
readlink -f
Verifies that the public command resolves to the managed snap target.
certbot --version
Records the actual release rather than relying on tutorial prose.
Command
sudo snap install --classic certbot && if test ! -e /usr/local/bin/certbot; then sudo ln -s /snap/bin/certbot /usr/local/bin/certbot; fi && test "$(readlink /usr/local/bin/certbot)" = /snap/bin/certbot && certbot --version
Example output / evidence
certbot 5.x (exact stable release may differ)

Checkpoint: Checkpoint: install

Continue whenExactly one intended Certbot binary runs from the stable snap, its exact version is recorded, and no competing renewal timer remains.

Stop whenAnother installation method, unexpected symlink, unapproved snap source, failed refresh, or duplicate timer exists.

If this step fails

The symlink command fails because `/usr/local/bin/certbot` exists.

Likely causeAn earlier package, wrapper, or manual installation already owns the path.

Safe checks
  • command -v certbot
  • readlink -f /usr/local/bin/certbot
  • dpkg -S /usr/bin/certbot 2>/dev/null || true
  • snap list certbot

ResolutionIdentify and deliberately migrate the existing installation; do not force-overwrite the path.

Security notes

  • Classic confinement grants broad host access. Install only the official signed snap and restrict who can invoke privileged issuance.

Alternatives

  • Use a distribution package only when organizational policy owns its patch and renewal lifecycle; do not combine methods.

Stop conditions

  • Another installation method, unexpected symlink, unapproved snap source, failed refresh, or duplicate timer exists.
04

warning

Issue and install the certificate

danger

Request only names already present in DNS, supply a monitored operations address, accept the terms explicitly, and let the Apache plugin create the TLS host.

Why this step matters

The issuance command creates a public CA order, stores a private key, and lets a privileged installer modify Apache, so names, email, terms, redirect, and diff must be explicit.

What to understand

Request only DNS names already proven in the virtual-host map. `--domain` is repeated intentionally because every SAN must be authorized.

Use a monitored address and review the terms outside the command. `--redirect` changes HTTP behavior and must match application and ACME policy.

Immediately inspect the generated file diff, lineage, and Apache parser result. Success text alone does not prove the correct vhost or externally served certificate.

System changes

  • Creates/updates an ACME account, private key and certificate lineage, Apache TLS configuration, redirect policy, and a reload.

Syntax explained

--apache
Uses Apache for challenge authentication and certificate installation.
--domain
Adds one requested certificate DNS name; repeat for each deliberate SAN.
--email
Registers the monitored ACME account contact.
--agree-tos
Records explicit acceptance of the CA subscriber agreement.
--no-eff-email
Declines unrelated email sharing.
--redirect
Configures HTTP-to-HTTPS redirection through the installer.
Command
Fill variables0/2 ready

Values stay on this page and are never sent or saved.

sudo certbot --apache --domain {{domain}} --domain www.{{domain}} --email {{opsEmail}} --agree-tos --no-eff-email --redirect
Example output / evidence
Successfully received certificate.
Successfully deployed certificate for example.com.
Congratulations! You have successfully enabled HTTPS.

Checkpoint: Checkpoint: issue

Continue whenAuthorization and issuance succeed for exactly the reviewed names, one expected lineage is created, and only intended Apache files change.

Stop whenCertbot proposes other names, selects another vhost, reports partial authorization, modifies unrelated sites, or Apache cannot parse the result.

If this step fails

Certbot cannot choose a virtual host for the requested name.

Likely causeServerName/ServerAlias is missing, duplicated, disabled, or defined only in an unexpected TLS site.

Safe checks
  • sudo apache2ctl -S
  • sudo grep -R -nE 'ServerName|ServerAlias' /etc/apache2/sites-enabled

ResolutionFix and parse the explicit name-based virtual-host map first, then retry the same reviewed order.

Security notes

  • The command runs a root-capable installer and creates a private key. Protect terminal logs and do not add secret flags or DNS tokens to shell history.

Alternatives

  • Use `certbot certonly --apache` when configuration installation must be performed manually after a separate review.

Stop conditions

  • Certbot proposes other names, selects another vhost, reports partial authorization, modifies unrelated sites, or Apache cannot parse the result.
05

verification

Inspect the generated lineage and Apache files

read-only

Confirm certificate names, expiry, key paths, enabled SSL module, and the resulting virtual-host map without printing private key material.

Why this step matters

A lineage inventory and Apache configuration map connect Certbot's success message to the actual certificate files and enabled TLS host without exposing key contents.

What to understand

Record certificate name, SANs, expiry, authenticator, installer, and live paths. Confirm the SSL module and all address/name mappings.

Inspect only directive paths and public certificate metadata. Never cat or hash the private key into a broadly accessible ticket.

Compare changed Apache files with the archive and ensure no global default or other tenant acquired the certificate or redirect.

System changes

  • No persistent change; reads public metadata and configuration paths.

Syntax explained

certbot certificates
Displays Certbot-managed lineages, names, expiry, and paths without printing private key bytes.
apache2ctl -M
Lists loaded modules and proves mod_ssl is active.
apache2ctl -S
Shows the complete HTTP/TLS virtual-host selection map.
grep SSLCertificate
Locates Apache certificate path references for review.
Command
sudo certbot certificates && sudo apache2ctl -M | grep ssl && sudo apache2ctl -S && sudo grep -R 'SSLCertificate' /etc/apache2/sites-enabled
Example output / evidence
Certificate Name: example.com
Expiry Date: 2026-10-23
ssl_module (shared)
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem

Checkpoint: Checkpoint: inspect

Continue whenExactly one intended lineage covers every requested name, Apache references stable live paths, and the TLS vhost map has no unexpected default or overlap.

Stop whenNames, expiry, paths, file permissions, modules, vhost ordering, or changed-file scope is unexpected.

If this step fails

The lineage exists but no enabled Apache file references it.

Likely causeAuthentication succeeded but installation failed, the wrong installer was used, or a later rollback removed the generated site.

Safe checks
  • sudo certbot certificates
  • sudo grep -R SSLCertificate /etc/apache2
  • sudo apache2ctl -S

ResolutionKeep HTTP service stable, repair the explicit TLS vhost or rerun the installer only after reviewing the intended site.

Security notes

  • Operational evidence should include public certificate paths and metadata, never private-key contents.

Alternatives

  • Use `openssl x509` on `cert.pem` for machine-readable public metadata when building monitoring.

Stop conditions

  • Names, expiry, paths, file permissions, modules, vhost ordering, or changed-file scope is unexpected.
06

verification

Validate redirect, hostname, and certificate chain

read-only

Check the HTTP redirect, HTTPS status, negotiated protocol, certificate subject, issuer, and expiry from the served endpoint.

Why this step matters

A successful local reload does not prove public DNS, redirect behavior, SNI selection, full-chain delivery, hostname coverage, or the response from the real ingress.

What to understand

Test HTTP and HTTPS separately and inspect Location rather than following it blindly; unexpected cross-domain redirects can expose users to another tenant.

Supply `-servername` to OpenSSL because shared listeners select certificates through SNI. Inspect subject alternative names in addition to the common name.

Repeat from an external IPv4 and IPv6 network and at any upstream TLS termination point. A local loopback test can bypass the actual edge.

System changes

  • No persistent change; creates client connections and reads public certificate metadata.

Syntax explained

curl --head
Fetches response headers without downloading the body.
curl --fail
Treats HTTP 4xx/5xx as command failure.
openssl s_client -servername
Performs a TLS handshake with the explicit SNI hostname.
openssl x509 -noout
Prints selected public certificate metadata instead of encoded certificate content.
Command
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

curl --head http://{{domain}}/ && curl --fail --head https://{{domain}}/ && echo | openssl s_client -connect {{domain}}:443 -servername {{domain}} 2>/dev/null | openssl x509 -noout -subject -issuer -dates
Example output / evidence
HTTP/1.1 301 Moved Permanently
Location: https://example.com/
HTTP/2 200
subject=CN=example.com
notAfter=...

Checkpoint: Checkpoint: protocol test

Continue whenHTTP redirects only to the same HTTPS hostname, HTTPS succeeds, and the served certificate has the expected issuer, validity, and SANs.

Stop whenAny address times out, redirect target differs, chain verification fails, certificate name/expiry is wrong, or the application response regresses.

If this step fails

OpenSSL shows the Apache default certificate rather than the new lineage.

Likely causeThe client omitted SNI, the TLS vhost map is ambiguous, or DNS reaches a different termination point.

Safe checks
  • sudo apache2ctl -S
  • echo | openssl s_client -connect app.example.com:443 -servername app.example.com 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName

ResolutionCorrect SNI/vhost mapping at the real TLS endpoint and retest every requested name externally.

Security notes

  • Do not weaken certificate verification to make a probe green; investigate the hostname, trust path, and serving endpoint.

Alternatives

  • Use an approved external synthetic monitor to exercise the same DNS and edge path continuously.

Stop conditions

  • Any address times out, redirect target differs, chain verification fails, certificate name/expiry is wrong, or the application response regresses.
07

verification

Test unattended renewal

read-only

Run Certbot's dry-run against the staging CA and verify the packaged timer; a successful initial issue is incomplete until renewal is proven.

Why this step matters

Public HTTPS is temporary unless saved renewal settings, challenge reachability, plugin availability, timer execution, and deployment hooks work unattended.

What to understand

A dry run uses the staging CA and exercises saved lineage configuration. It can still create network traffic and temporary challenge state, so run it during a reviewed window.

Inspect both snap timer/service units and the latest logs. Record the next timer activation while remembering that Certbot may randomize execution.

Dry-run success is a checkpoint, not perpetual proof; DNS, firewalls, plugins, and vhost paths can drift before the next real renewal.

System changes

  • Contacts the staging ACME service and may create temporary challenge/configuration state that Certbot removes; it should not replace the production lineage.

Syntax explained

renew
Processes managed lineages according to saved renewal configuration.
--dry-run
Exercises eligible renewals against staging without deploying a production certificate.
systemctl list-timers --all
Shows scheduled and previous timer execution state.
Command
sudo certbot renew --dry-run && systemctl list-timers --all | grep -E 'certbot|snap.certbot.renew'
Example output / evidence
Congratulations, all simulated renewals succeeded.
certbot.timer loaded active waiting

Checkpoint: Checkpoint: renewal

Continue whenAll simulated renewals succeed and exactly one intended Certbot timer/service lifecycle is present with inspectable logs.

Stop whenAny authorization, plugin, parser, deployment, timer, or cleanup error occurs, or the dry run changes the production certificate.

If this step fails

Dry run fails with connection or authorization errors months after initial issuance.

Likely causeDNS, IPv6, firewall, challenge routing, plugin location, or renewal configuration drifted.

Safe checks
  • sudo certbot renew --dry-run -v
  • systemctl status snap.certbot.renew.timer
  • sudo journalctl -u snap.certbot.renew.service -n 100 --no-pager

ResolutionRepair the saved method and external path now, repeat staging rehearsal, and verify production expiry remains unchanged.

Security notes

  • Do not place DNS credentials on a command line; official DNS plugins should read a root-only credential file with minimal API scope.

Alternatives

  • For a deliberate method migration, amend one lineage and prove it with a dry run before changing all certificates.

Stop conditions

  • Any authorization, plugin, parser, deployment, timer, or cleanup error occurs, or the dry run changes the production certificate.
08

instruction

Record expiry monitoring and incident commands

read-only

Capture a machine-readable expiry date, recent timer results, and the safe certificate inventory commands for the operations runbook.

Why this step matters

Renewal automation can fail silently until an outage unless expiry, service failures, and the public endpoint are observed independently with actionable lead time.

What to understand

Read the public cert expiry from the stable lineage and convert it to a machine-readable timestamp; alert well before operational response time becomes risky.

Monitor timer/service exit state and the externally served certificate, because a renewed file may not be the certificate delivered by an upstream edge.

Record inventory, logs, owners, escalation, and the safe manual dry-run command in the incident runbook without copying private key data.

System changes

  • No persistent change in the supplied command; the resulting monitoring integration is an operational configuration change.

Syntax explained

openssl x509 -enddate -noout
Reads only the public certificate expiration field.
date -d
Parses the returned date and emits an ISO timestamp.
journalctl -u
Reads service-specific renewal events.
certbot certificates
Provides authoritative local lineage inventory.
Command
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

date -d "$(sudo openssl x509 -enddate -noout -in /etc/letsencrypt/live/{{domain}}/cert.pem | cut -d= -f2)" --iso-8601=seconds && sudo journalctl -u certbot.service -n 30 --no-pager && sudo certbot certificates
Example output / evidence
2026-10-23T12:00:00+00:00
No renewals were attempted.

Checkpoint: Checkpoint: monitor

Continue whenExpiry is parseable, sufficiently distant, renewal logs are healthy, and an external probe observes the same certificate.

Stop whenExpiry cannot be read, monitoring sees another certificate, renewal failures exist, alert ownership is absent, or lead time violates policy.

If this step fails

The local lineage has a new expiry but clients still receive the old certificate.

Likely causeApache was not reloaded, another TLS terminator serves the connection, or monitoring targets a different address family.

Safe checks
  • sudo certbot certificates
  • echo | openssl s_client -connect app.example.com:443 -servername app.example.com 2>/dev/null | openssl x509 -noout -serial -enddate
  • sudo apache2ctl -S

ResolutionFind the real termination point, deploy/reload there after syntax validation, and compare serial plus expiry externally.

Security notes

  • Monitoring needs public certificate metadata and service status, not access to private-key bytes.

Alternatives

  • Use an approved certificate transparency/endpoint monitor in addition to local timer alerts for independent evidence.

Stop conditions

  • Expiry cannot be read, monitoring sees another certificate, renewal failures exist, alert ownership is absent, or lead time violates policy.

Finish line

Verification checklist

Public HTTPScurl --fail --head https://{{domain}}/Returns a successful response with no certificate error.
Renewal rehearsalsudo certbot renew --dry-runAll simulated renewals succeed.
Apache syntaxsudo apache2ctl configtestSyntax OK after Certbot edits.

Recovery guidance

Common problems and safe checks

Certbot reports that authorization failed or the challenge returned 404.

Likely causeDNS points elsewhere, the request selects another vhost, the challenge path is rewritten/proxied, or public port 80 does not reach this Apache instance.

Safe checks
  • getent ahosts app.example.com
  • sudo apache2ctl -S
  • curl -v http://app.example.com/.well-known/acme-challenge/oneliners-preflight
  • sudo tail -n 100 /var/log/apache2/app.example.com-access.log

ResolutionCorrect DNS and vhost routing or exempt the exact challenge path from an application rewrite, then prove the preflight object from an external network before retrying.

Authorization succeeds for IPv4 but fails intermittently.

Likely causeA published AAAA record reaches a different or unconfigured IPv6 listener.

Safe checks
  • dig +short A app.example.com
  • dig +short AAAA app.example.com
  • curl -6 -v http://app.example.com/.well-known/acme-challenge/oneliners-preflight

ResolutionConfigure the same site and firewall path over IPv6 or remove the inaccurate AAAA record according to change control before retrying.

Apache configtest fails after the installer change.

Likely causeA generated SSL vhost conflicts with existing directives, a referenced file is absent, or an earlier site configuration was already invalid.

Safe checks
  • sudo apache2ctl configtest
  • sudo apache2ctl -S
  • sudo certbot --apache rollback --checkpoints 1

ResolutionDo not reload. Review the changed site files, use Certbot's installer rollback or restore the archived Apache tree, and parse again before activation.

HTTPS presents a valid certificate for the wrong hostname.

Likely causeSNI maps to the wrong TLS virtual host, ServerName/ServerAlias overlaps, or a load balancer terminates TLS before Apache.

Safe checks
  • sudo apache2ctl -S
  • echo | openssl s_client -connect app.example.com:443 -servername app.example.com 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName

ResolutionCorrect the explicit TLS vhost map and certificate name at the actual termination boundary; do not rely on the first enabled SSL site.

`certbot renew --dry-run` fails although production HTTPS works.

Likely causeThe saved authenticator path, firewall, DNS, plugin installation, account, or renewal configuration no longer matches current service state.

Safe checks
  • sudo certbot certificates
  • sudo certbot renew --dry-run -v
  • sudo journalctl -u snap.certbot.renew.service -n 100 --no-pager

ResolutionRepair the saved challenge path or plugin state and repeat the staging rehearsal; do not wait for the production certificate to approach expiry.

Clients report an incomplete chain while local browsers appear healthy.

Likely causeApache references a leaf certificate rather than `fullchain.pem`, an intermediary proxy serves stale material, or a client lacks a required trust path.

Safe checks
  • sudo grep -R SSLCertificateFile /etc/apache2/sites-enabled
  • echo | openssl s_client -showcerts -connect app.example.com:443 -servername app.example.com

ResolutionReference the Certbot full chain at the actual TLS endpoint, reload only after configtest, and retest from an independent client.

After the procedure

Alternatives and next steps

Consider these alternatives

  • Run `certbot certonly --apache` and configure the TLS virtual host manually when change control requires the authenticator to avoid installer edits.
  • Use the webroot authenticator when the challenge directory is stable and explicit, particularly behind a carefully understood proxy or multi-node frontend.
  • Use an official DNS plugin for DNS-01 when issuing wildcards or when public HTTP cannot reach the server; store narrowly scoped credentials outside served paths.
  • Terminate TLS at an approved load balancer only when certificate ownership, backend encryption, client-address trust, renewal, and rollback are documented at that boundary.

Operate it safely

  • Create an expiry alert with enough lead time to repair DNS, firewall, or authenticator failures and alert on failed renewal service executions.
  • Test every certificate name externally over IPv4 and IPv6, including HTTP redirect, SNI, chain, protocol, and application health.
  • Document key-compromise response: containment, revocation reason, replacement lineage, application reload, validation, and audit evidence.
  • Review TLS protocols, ciphers, OCSP behavior, HSTS readiness, and proxy/client-IP trust using current Apache and organizational policy.
  • Automate the same configtest, dry run, certificate inventory, and external probe through change management without copying private key data.

Reference

Frequently asked questions

Why keep port 80 after enabling HTTPS?

It normally provides a harmless redirect and remains the validation path for HTTP-01 renewal. Closing it without changing the saved authenticator can break future renewals.

Does `renew --dry-run` shorten or replace the real certificate?

No. It uses the staging environment to rehearse saved renewal behavior and should leave the production lineage unchanged.

Should rollback revoke the certificate?

Usually not. Restore Apache service first. Revoke only when the key or authorization should no longer be trusted, then delete a lineage only after all consumers are retired.

Recovery

Rollback

Revert Apache's TLS host from the saved archive; revoke only when the certificate key or authorization must no longer be trusted.

  1. Use certbot --apache rollback for the most recent installer change when available, then run apache2ctl configtest.
  2. Restore /etc/apache2 from /root/apache-before-certbot.tgz if the installer rollback is insufficient.
  3. Keep the certificate lineage while diagnosing; use certbot revoke and certbot delete only for deliberate retirement or compromise.
  4. Remove the HTTPS firewall rule only after confirming no production client depends on port 443.

Evidence

Sources and review

Verified 2026-07-25Review due 2026-10-23
Official Certbot installation instructionsofficialCertbot user guideofficialApache SSL/TLS encryptionofficialApache HTTP Server 2.4 documentationofficial