Skip to content

Deployment & Production

html2rss-web ships on Docker Hub. Start with the Getting Started guide, which uses docker-compose.quickstart.yml. Make sure the one-file demo works first, then add the production pieces below.

The examples use html2rss/web:1, the recommended major-version tag. Pin an exact release if your deployment process requires it.

There are two materially different deployment modes:

  • Automatic generation enabled: primary self-hosted workflow, requires AUTO_SOURCE_ENABLED=true and HTML2RSS_ACCESS_TOKEN
  • Included feeds fallback only: lower-maintenance path when the Feed Directory already covers your needs

If you do not need page-URL generation yet, keep AUTO_SOURCE_ENABLED off and ship the fallback mode only.

Before exposing html2rss-web, ensure:

  • Your domain (for example yourdomain.com) resolves to the host running Docker
  • Inbound TCP ports 80 and 443 reach the host (check firewalls and cloud security groups)
  • You are ready to watch the first deployment logs for certificate issuance
  • You have a value ready for HTML2RSS_SECRET_KEY
  • You have a value ready for HTML2RSS_ACCESS_TOKEN

If you plan to enable automatic feed generation, also prepare:

  • a clear way to give users the same HTML2RSS_ACCESS_TOKEN your instance expects
  • optional advanced-rendering infrastructure only if harder sites later prove they need it

A reverse proxy terminates public HTTPS traffic and forwards requests to html2rss-web on your private Docker network.

Caddy handles certificates and redirects.

services:
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- caddy_data:/data
command:
- caddy
- reverse-proxy
- --from
- ${CADDY_HOST}
- --to
- html2rss-web:4000
html2rss-web:
image: html2rss/web:1
restart: unless-stopped
env_file:
- path: .env
required: false
environment:
RACK_ENV: production
PORT: 4000
HTML2RSS_SECRET_KEY: ${HTML2RSS_SECRET_KEY:?set HTML2RSS_SECRET_KEY}
HTML2RSS_ACCESS_TOKEN: ${HTML2RSS_ACCESS_TOKEN:?set HTML2RSS_ACCESS_TOKEN}
AUTO_SOURCE_ENABLED: "true"
SENTRY_DSN: ${SENTRY_DSN:-}
BOTASAURUS_SCRAPER_URL: http://botasaurus:4010
botasaurus:
image: html2rss/botasaurus-scrape-api:latest
restart: unless-stopped
volumes:
caddy_data:

Create a .env file beside your compose file:

CADDY_HOST=yourdomain.com
HTML2RSS_SECRET_KEY=<openssl rand -hex 32>
HTML2RSS_ACCESS_TOKEN=<strong access token>
# Optional only if you want authenticated GET /api/v1/health
# HEALTH_CHECK_TOKEN=<strong health token>

Before starting the stack:

  • Set CADDY_HOST for your domain.
  • Generate HTML2RSS_SECRET_KEY with openssl rand -hex 32.
  • Set a strong HTML2RSS_ACCESS_TOKEN. This is the token users paste into the web UI.
  • Leave HEALTH_CHECK_TOKEN unset unless you intentionally use authenticated GET /api/v1/health.
  • Leave BUILD_TAG and GIT_SHA unset unless you intentionally override image metadata in logs.
  • Adjust optional knobs such as AUTO_SOURCE_ENABLED and SENTRY_DSN as needed; refer to the environment reference for details.

After docker compose up -d, run docker compose logs caddy --tail 20; look for certificate obtained.

Re-test after DNS changes with SSL Labs.

Harden the application before inviting other users:

  • Set a strong HTML2RSS_ACCESS_TOKEN and rotate it when needed
  • Prefer environment files (.env) stored outside version control for secrets
  • Keep token sharing/distribution outside public docs and outside version control
  • Give trusted users the same current HTML2RSS_ACCESS_TOKEN through your normal operator channel
  • If you use authenticated GET /api/v1/health, set a strong HEALTH_CHECK_TOKEN; do not leave CHANGE_ME_HEALTH_CHECK_TOKEN anywhere in production
services:
html2rss-web:
image: html2rss/web:1
restart: unless-stopped
env_file:
- path: .env
required: false
environment:
RACK_ENV: production
PORT: 4000
HTML2RSS_SECRET_KEY: ${HTML2RSS_SECRET_KEY:?set HTML2RSS_SECRET_KEY}
HTML2RSS_ACCESS_TOKEN: ${HTML2RSS_ACCESS_TOKEN:?set HTML2RSS_ACCESS_TOKEN}
AUTO_SOURCE_ENABLED: "true"
SENTRY_DSN: ${SENTRY_DSN:-}
BOTASAURUS_SCRAPER_URL: http://botasaurus:4010
botasaurus:
image: html2rss/botasaurus-scrape-api:latest
restart: unless-stopped

Store these variables in a .env file and reference it with env_file: as demonstrated in the Caddy example.

Keep the instance healthy once it is in production:

  • Review docker compose logs regularly for feed errors or certificate renewals
  • Enable automatic image updates for the Docker tag you selected
  • Right-size CPU and memory to avoid starvation when parsing large feeds
  • Use GET /api/v1/health/ready for standard readiness checks
  • Add authenticated GET /api/v1/health only when your operator tooling needs it
services:
watchtower:
image: containrrr/watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
# Optional for private registries only:
# - "${HOME}/.docker/config.json:/config.json:ro"
command: --cleanup --interval 7200 html2rss-web botasaurus caddy

This Watchtower shape scopes updates to html2rss-web, botasaurus, and caddy; change the service names if your stack differs.

Check docker compose logs watchtower occasionally to confirm updates are applied.

services:
html2rss-web:
image: html2rss/web:1
deploy:
resources:
limits:
memory: 512M
cpus: "0.5"
reservations:
memory: 256M
cpus: "0.25"

Adjust limits to match host capacity. Increase memory for large feeds.

  • Test different feed sources before inviting external users
  • Publish /openapi.yaml from the running instance if you expect agents or integrations to call the API directly
  • Add your instance to the community wiki if you want it listed publicly
  • Visit the troubleshooting guide or join the community discussions when you need help
  • Ready to contribute fixes or docs? See the contributing guide