Custom Proxy
When you deploy an app, Potions creates the Caddy site blocks that serve it. Each one forwards requests to your app with a single line, reverse_proxy 127.0.0.1:<port>, pointed at whichever slot is serving. A custom proxy replaces that line with Caddy directives of your own, for upstreams the default can't express, such as an HTTP/3 origin or a separate route for WebSockets.
Everything else stays with Potions: site addresses, certificates, redirects between your domains, and the switch between slots on every deploy and rollback. Your directives go into every Caddy file Potions writes for the app, so they survive deploys, rollbacks and domain changes.
Setting It Up
On the app's Settings tab, in the Custom proxy section, turn on Use a custom proxy, enter your directives and click Save. Preview the Caddy file shows the file they produce for the slot serving now.
To go back to the default proxy, turn the setting off and save. Your directives are kept.
Custom proxies aren't available for apps behind a load balancer, which includes clustered apps.
Placeholders
Your app runs in two slots, blue and green, each on its own PORT, and every deploy and rollback switches to the other one. Write placeholders instead of port numbers so your directives follow the switch:
| Placeholder | Becomes |
|---|---|
{{port}} |
The serving slot's PORT |
{{port+N}} |
The serving slot's PORT plus N, for a second listener your app derives from PORT |
Every placeholder is filled in with the new slot's ports before traffic switches, so all of them move together. Directives must use at least one.
Example: HTTP/3 to Your App
This sends normal requests to an HTTP/3 listener your app runs on PORT + 10000, and Phoenix Channels to the app's regular PORT. For an app on ports 4004 and 4005, {{port+10000}} becomes 14004 or 14005.
handle /socket/* {
reverse_proxy 127.0.0.1:{{port}}
}
handle {
reverse_proxy https://127.0.0.1:{{port+10000}} {
header_down -Alt-Svc
transport http {
versions 3
tls_server_name localhost
tls_trusted_ca_certs /etc/caddy/my-app-origin-ca.pem
}
}
}
-
Every release must start the HTTP/3 listener. The deploy health check only requests the regular
PORT, so a release that fails to start the listener still goes live, and these routes return errors. -
Caddy must trust the listener's certificate, so the CA file has to be readable by the
caddyuser. -
header_down -Alt-Svckeeps your app's private port out of the publicAlt-Svcheader. Caddy still advertises HTTP/3 on port 443. - Caddy documents HTTP/3 upstreams as experimental.
-
For HTTP/3 all the way from the browser, use a custom domain. Platform
onpotions.comdomains go through Cloudflare, which doesn't use HTTP/3 to origins.
What Happens When You Save
- Potions checks your directives as you type: placeholders are valid, braces and quotes are balanced, and nothing ends the site block early.
- Caddy validates the server's whole config with your directives in place before reloading. If Caddy rejects them, nothing changes and you see Caddy's error.
- For a running app, Potions then requests your health check path through Caddy. If Caddy can't reach your app (a 502, a 504 or no answer), Potions puts the previous config back and doesn't save the setting. Until then, requests get the same errors, usually for a few seconds, and for up to about half a minute if nothing answers at all.
Saving reloads Caddy, which closes open WebSocket connections on the server. LiveView clients reconnect on their own.
During Deploys and Rollbacks
Every deploy and rollback writes your directives with the new slot's ports, and Caddy validates them before traffic switches. If Caddy rejects them, for example because a certificate file they reference was removed, the deploy fails with Caddy's error and your current release keeps serving.