Skip to content

Deployment & Production

Start from a working Getting Started demo, then switch to production Compose with your own secrets.

Use image tag html2rss/web:1 (or pin an exact release). Production Compose lives in html2rss-web/docker-compose.yml with .env.example.

  1. Download and copy env:
Terminal window
curl -O https://raw.githubusercontent.com/html2rss/html2rss-web/main/docker-compose.yml
curl -O https://raw.githubusercontent.com/html2rss/html2rss-web/main/.env.example
cp .env.example .env
  1. Set secrets in .env:
Terminal window
openssl rand -hex 32 # HTML2RSS_SECRET_KEY
openssl rand -hex 32 # HTML2RSS_ACCESS_TOKEN
Variable Required Notes
HTML2RSS_SECRET_KEY Yes ≥ 32 characters
HTML2RSS_ACCESS_TOKEN Yes ≥ 16 characters; paste into the UI
AUTO_SOURCE_ENABLED Default true Keep true for URL-to-RSS
HEALTH_CHECK_TOKEN No Only if you use authenticated GET /api/v1/health
SENTRY_DSN / BOTASAURUS_SENTRY_DSN No Separate web vs scraper Sentry projects
  1. Start:
Terminal window
docker compose up -d

Web listens on host port 4000 (4000:4000). Botasaurus stays on 127.0.0.1:4010 inside the published mapping from Compose. Open http://localhost:4000/ or http://<LAN_IP>:4000/ and paste HTML2RSS_ACCESS_TOKEN.

Port 4000 on all interfaces is intentional for LAN access. Protect the instance with a strong access token; do not expose an unauthenticated demo token on a shared network.

No reverse proxy required. The app does not force HTTPS upgrades on plain HTTP, so LAN IPs keep working. HSTS and CSP upgrade-insecure-requests apply only on HTTPS requests.

Terminate TLS in front of the app and forward to port 4000:

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:

Example .env beside Compose:

CADDY_HOST=yourdomain.com
HTML2RSS_SECRET_KEY=<openssl rand -hex 32>
HTML2RSS_ACCESS_TOKEN=<openssl rand -hex 32>
# Optional:
# HEALTH_CHECK_TOKEN=
# SENTRY_DSN=
# BOTASAURUS_SENTRY_DSN=

After docker compose up -d, check docker compose logs caddy --tail 20 for certificate issuance.

Auto-source is the default. To add curated YAML feeds, bind-mount feeds.yml (see Advanced Feeds):

volumes:
- type: bind
source: ./config/feeds.yml
target: /app/config/feeds.yml
read_only: true
  • GET /api/v1/health/ready for readiness
  • Authenticated GET /api/v1/health only when HEALTH_CHECK_TOKEN is set
  • OpenAPI at /openapi.yaml on the running instance
  • Monitoring reference for Sentry and probes
  • Environment variables for the full table