Symptoms IRC clients show “connection to server lost (bytes remaining on stream)” errors repeatedly.
Cause Attempting to proxy IRC protocol through Caddy using HTTP-based directives.
What Did Not Work
1. **Caddy reverse_proxy** - reverse_proxy is designed for HTTP, not raw TCP - IRC is a long-lived, bidirectional, line-oriented protocol - This causes stream corruption and connection drops
2. **Caddy tcp_proxy** - tcp_proxy is not a valid Caddy directive in standard builds - Would require custom Caddy build with layer4 module (mholt/caddy-l4) - Even with proper module, Caddy has known issues with TCP stream corruption
What Finally Worked
Ergo native TLS termination with certificate sync from Caddy.
Implementation
1. **docker-compose.yml changes** - Expose port 6697 from Ergo directly, not through Caddy - Remove port 6697 from Caddy - Add certificate volume mount to Ergo: ./irc/certs:/ircd/certs:ro
2. **ircd.yaml configuration**
- Configure Ergo to listen on 127.0.0.1:6667 for The Lounge (plaintext, internal)
- Configure Ergo to listen on :6697 with TLS certificates
- TLS certificates specified in listeners section:
```yaml
":6697":
tls-certificates:
- cert: /ircd/certs/fullchain.pem
key: /ircd/certs/privkey.pem
```
3. **Certificate sync script** - Create sync-irc-certs.sh to copy certificates from Caddy to Ergo - Caddy stores certificates at /data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/irc-direct.folk.zone/ - Script copies .crt to fullchain.pem and .key to privkey.pem - Script sends SIGHUP to Ergo to reload certificates without dropping connections - Add to cron for automatic renewals
4. **Caddyfile** - Remove IRC proxy block entirely - Keep irc-direct.folk.zone info page for certificate renewal - Caddy continues to handle HTTP for The Lounge and other services
Benefits
Prevention Use native TLS termination for IRC daemons instead of proxying through HTTP reverse proxies. IRC is a TCP protocol, not HTTP.
Last updated: 2026-06-21