HTTPS for IP Addresses, No DNS Required
The serversideup/docker-php images shipped v4.6.0-beta1 with a new CADDY_ACME_PROFILE environment variable. Set it to shortlived and a FrankenPHP container reachable only by IP address can obtain a real, browser-trusted TLS certificate from Let's Encrypt — no domain name, no DNS record, no self-signed warning.
Let's Encrypt made 6-day certificates and IP-address certificates generally available in January 2026. The shortlived profile is the only one under which Let's Encrypt issues IP certificates, so the two features arrive together.
How the Short-Lived Certificate Profile Works
Setting CADDY_ACME_PROFILE=shortlived writes the following snippet into the Caddy configuration:
cert_issuer acme {
profile shortlived
}
Caddy then requests certificates valid for 160 hours (just under seven days), renewing them every two days. That lifetime qualifies as a Short-Lived Subscriber Certificate under CA/Browser Forum rules, which means revocation information is less critical — a compromised key becomes useless in days rather than months.
A few operational trade-offs to keep in mind:
- Using any explicit profile drops the ZeroSSL fallback the images normally keep.
- If the container loses outbound access to Let's Encrypt's ACME API, you have roughly four and a half days before the certificate expires.
- Let's Encrypt allows five certificates per identical identifier set every seven days, so repeated redeploys without persistent volumes can exhaust that quota quickly.
Configuring It in Docker Compose
The feature is only in the beta tags. Pin to v4.6.0-beta1 rather than the stable 8.4-frankenphp tag:
services:
php:
image: serversideup/php:8.4-frankenphp-v4.6.0-beta1
ports:
- "80:8080"
- "443:8443"
volumes:
- caddy-config:/config
- caddy-data:/data
environment:
SSL_MODE: "full"
CADDY_AUTO_HTTPS: "on"
CADDY_ACME_PROFILE: "shortlived"
CADDY_HTTPS_SERVER_ADDRESS: "https://203.0.113.10"
CADDY_GLOBAL_OPTIONS: "default_sni example.com"
volumes:
caddy-config:
caddy-data:
Key variables explained:
CADDY_AUTO_HTTPS: "on"— enables Caddy's automatic HTTPS (off by default).SSL_MODE: "full"— serves the app over HTTPS and redirects HTTP with a 308.CADDY_HTTPS_SERVER_ADDRESS— the public IP address Caddy should obtain a certificate for.CADDY_GLOBAL_OPTIONS: "default_sni example.com"— provides a fallback SNI identity, because a client connecting to a bare IP sends no Server Name Indication field.
Both ports 80 and 443 (mapped from 8080/8443 inside the unprivileged container) must be reachable from the internet for the ACME challenge to complete.
Mount /config and /data as named volumes. Certificates and ACME account state live there. A container that discards them on every restart will request fresh certificates on every restart, burning through the rate limit fast.
IPv6 Note
Caddy fixed IPv6 handling for IP certificates in issue #7399, closed in April 2026. Caddy v2.11.4 (embedded in the current images) includes that fix, but IPv4 has a longer track record — test IPv6 thoroughly before relying on it in production.
Other Changes in v4.6.0-beta1
TRUSTED_PROXYnow behaves consistently across FrankenPHP, NGINX, and Apache, sorequest()->ip()returns the visitor IP regardless of which server variant is running.- A new
caddyfile-global.ddirectory lets you drop custom global Caddy config snippets. healthcheck-nightwatchintegrates Laravel Nightwatch status checks with DockerHEALTHCHECK.AUTORUN_LARAVEL_SKIP_IF_NOT_FOUNDallows a container withAUTORUN_ENABLED=trueto exit cleanly when Laravel is not yet installed.
Key Takeaways
CADDY_ACME_PROFILE=shortlivedis the single variable that unlocks both short-lived and IP-address certificates.- No DNS is required — ideal for staging boxes, internal APIs, VPN-only servers, and demo droplets.
- Certificates last 160 hours and renew every two days; persistent volumes are essential to avoid rate-limit exhaustion.
- The feature is in a prerelease tag; do not use it in production environments you cannot afford to break.
- ZeroSSL fallback is disabled when any explicit ACME profile is set.
Source: Let's Encrypt HTTPS on an IP Address With FrankenPHP — Laravel News