Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default defineConfig({
"/components/html2rss-configs": "/creating-custom-feeds/",
"/components": "/",
"/web-application/how-to/deployment": "/web-application/deployment/",
"/web-application/how-to/automatic-updates": "/web-application/deployment/#auto-update-with-watchtower",
"/web-application/how-to/automatic-updates": "/web-application/deployment/#updating-the-stack",
"/web-application/how-to/use-automatic-feed-generation":
"/web-application/guides/use-the-feed-directory/",
"/web-application/how-to/use-automatic-feed-generation/":
Expand Down
117 changes: 72 additions & 45 deletions src/components/docs/DockerComposeSnippet.astro
Original file line number Diff line number Diff line change
@@ -1,42 +1,85 @@
---
import { Code } from "@astrojs/starlight/components";
import { botasaurusImage, caddyImage, watchtowerImage, webImage } from "../../data/docker";
import { botasaurusImage, browserlessImage, caddyImage, webImage } from "../../data/docker";

interface Props {
variant: "minimal" | "productionCaddy" | "secure" | "watchtower" | "resourceGuardrails";
variant: "minimal" | "productionCaddy" | "resourceGuardrails";
}

const { variant } = Astro.props;

const snippets: Record<Props["variant"], string> = {
minimal: `services:
html2rss-web:
image: ${webImage}
caddy:
image: ${caddyImage}
restart: unless-stopped
depends_on:
- html2rss-web
ports:
- "127.0.0.1:4000:4000"
volumes:
- caddy_data:/data
- caddy_config:/config
command:
- sh
- -c
- |
caddy run --config - --adapter caddyfile <<EOF
{
admin off
\${CADDY_GLOBAL_OPTIONS:-}
}
https://localhost:4000 {
reverse_proxy html2rss-web:4000 {
header_up X-Forwarded-For {client_ip}
header_up X-Real-IP {client_ip}
header_down -Via
}
}
EOF

html2rss-web:
image: ${webImage}
environment:
RACK_ENV: development
HTML2RSS_ACCESS_TOKEN: CHANGE_ME_ADMIN_TOKEN
BOTASAURUS_SCRAPER_URL: http://botasaurus:4010

botasaurus:
image: ${botasaurusImage}`,
image: ${botasaurusImage}

volumes:
caddy_data:
caddy_config:`,
productionCaddy: `services:
caddy:
image: ${caddyImage}
restart: unless-stopped
depends_on:
- html2rss-web
ports:
- "80:80"
- "443:443"
volumes:
- caddy_data:/data
- caddy_config:/config
command:
- caddy
- reverse-proxy
- --from
- \${CADDY_HOST}
- --to
- html2rss-web:4000
- sh
- -c
- |
caddy run --config - --adapter caddyfile <<EOF
{
admin off
\${CADDY_GLOBAL_OPTIONS:-}
}
https://\${CADDY_HOST:-localhost} {
reverse_proxy html2rss-web:4000 {
header_up X-Forwarded-For {client_ip}
header_up X-Real-IP {client_ip}
header_down -Via
}
}
EOF

html2rss-web:
image: ${webImage}
Expand All @@ -47,46 +90,30 @@ const snippets: Record<Props["variant"], string> = {
environment:
RACK_ENV: production
PORT: 4000
AUTO_SOURCE_ENABLED: \${AUTO_SOURCE_ENABLED:-false}
HTML2RSS_ACCESS_TOKEN: \${HTML2RSS_ACCESS_TOKEN:-}
HTML2RSS_SECRET_KEY: \${HTML2RSS_SECRET_KEY:?set HTML2RSS_SECRET_KEY}
HTML2RSS_ACCESS_TOKEN: \${HTML2RSS_ACCESS_TOKEN:?set HTML2RSS_ACCESS_TOKEN}
AUTO_SOURCE_ENABLED: "true"
HEALTH_CHECK_TOKEN: \${HEALTH_CHECK_TOKEN:?set HEALTH_CHECK_TOKEN}
SENTRY_DSN: \${SENTRY_DSN:-}
BOTASAURUS_SCRAPER_URL: http://botasaurus:4010

botasaurus:
image: ${botasaurusImage}
restart: unless-stopped
SENTRY_ENABLE_LOGS: \${SENTRY_ENABLE_LOGS:-false}
HTML2RSS_TOTAL_TIMEOUT_SECONDS: 25
RACK_TIMEOUT_SERVICE_TIMEOUT: 30
BROWSERLESS_IO_WEBSOCKET_URL: ws://browserless:4002
BROWSERLESS_IO_API_TOKEN: \${BROWSERLESS_IO_API_TOKEN:?set BROWSERLESS_IO_API_TOKEN}

volumes:
caddy_data:`,
secure: `services:
html2rss-web:
image: ${webImage}
browserless:
image: ${browserlessImage}
restart: unless-stopped
env_file:
- path: .env
required: false
ports:
- "127.0.0.1:4002:4002"
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
PORT: 4002
CONCURRENT: 10
TOKEN: \${BROWSERLESS_IO_API_TOKEN:?set BROWSERLESS_IO_API_TOKEN}

botasaurus:
image: ${botasaurusImage}
restart: unless-stopped`,
watchtower: `services:
watchtower:
image: ${watchtowerImage}
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`,
volumes:
caddy_data:
caddy_config:`,
resourceGuardrails: `services:
html2rss-web:
image: ${webImage}
Expand Down
29 changes: 13 additions & 16 deletions src/content/docs/web-application/deployment.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,9 @@ 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

### Why a Reverse Proxy?
### Configure Caddy & SSL (Automatic HTTPS)

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

### Option A: Caddy (Automatic HTTPS)

Caddy handles certificates and redirects.

<DockerComposeSnippet variant="productionCaddy" />
The default `docker-compose.yml` includes Caddy out-of-the-box as the entry point. Caddy automatically terminates public HTTPS traffic, manages SSL/TLS certificates (via Let's Encrypt / ZeroSSL), redirects HTTP to HTTPS, and sanitizes headers to mitigate IP spoofing rate-limit bypasses.

Create a `.env` file beside your compose file:

Expand All @@ -56,16 +50,19 @@ Create a `.env` file beside your compose file:
HTML2RSS_ACCESS_TOKEN=<strong access token>
# Optional only if you want authenticated GET /api/v1/health
# HEALTH_CHECK_TOKEN=<strong health token>
# Optional CDN/Load-balancer trusted proxies configuration
# CADDY_GLOBAL_OPTIONS="servers { trusted_proxies static 172.16.0.0/12 }"
`}
lang="dotenv"
/>

Before starting the stack:

- Set `CADDY_HOST` for your domain.
- Set `CADDY_HOST` for your domain. If unset or left as `localhost`, Caddy will run with a local self-signed certificate.
- 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`.
- If you run behind a CDN (like Cloudflare), define `CADDY_GLOBAL_OPTIONS` in your `.env` with your trusted proxy configurations.
- 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](/web-application/reference/env-variables/) for details.

Expand All @@ -83,27 +80,27 @@ Harden the application before inviting other users:
- 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

<DockerComposeSnippet variant="secure" />

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

## Operate & Monitor

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
- Keep your instance updated to pull the latest security fixes and features
- 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

### Auto-update with Watchtower
### Updating the Stack

<DockerComposeSnippet variant="watchtower" />
To update your containers to the latest version, run:

This Watchtower shape scopes updates to `html2rss-web`, `botasaurus`, and `caddy`; change the service names if your stack differs.
```sh
docker compose pull && docker compose up -d
```

Check `docker compose logs watchtower` occasionally to confirm updates are applied.
We recommend running the image with the `:1` major-version tag (e.g., `html2rss/web:1`). This ensures the `pull` command retrieves new patch and minor releases under major version 1, delivering bug fixes and improvements without the risk of breaking changes.

### Resource Guardrails

Expand Down
6 changes: 4 additions & 2 deletions src/content/docs/web-application/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Run `html2rss-web` locally with Docker, confirm one generated feed works, then m

After this guide, you should have:

- `html2rss-web` running at `http://localhost:4000`
- `html2rss-web` running at `https://localhost:4000`
- successful feed generated from your own page URL
- a clear handoff from the demo stack to the real deployment setup
- a generated feed URL from your own page URL
Expand Down Expand Up @@ -59,7 +59,9 @@ This first-run stack keeps the path narrow:

## First Success Check

1. Open `http://localhost:4000`
1. Open `https://localhost:4000`
> [!NOTE]
> Because the local stack runs securely over HTTPS using Caddy's internal certificate authority, your browser will show a warning about an untrusted or self-signed certificate. You can safely bypass/accept the warning to access the interface.
2. Paste your own page URL into `Page URL`
3. Start with a listing, newsroom, changelog, releases, or updates page instead of a homepage
4. Enter `CHANGE_ME_ADMIN_TOKEN` when prompted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ Use an exact version tag when you need fully pinned deploys. Use `latest` only w

Release images include SBOM and provenance metadata. The image build also sets `BUILD_TAG` to the release version and `GIT_SHA` to the released commit.

If you use Docker Compose, [set up Watchtower](/web-application/deployment/#auto-update-with-watchtower) to pull updates for the tag you selected.
If you use Docker Compose, refer to [Updating the Stack](/web-application/deployment/#updating-the-stack) to pull updates for the tag you selected.
1 change: 0 additions & 1 deletion src/data/docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ export const dockerHubUrl = `https://hub.docker.com/r/${dockerHubRepository}`;
export const webImage = `${dockerHubRepository}:1`;
export const browserlessImage = 'ghcr.io/browserless/chromium';
export const caddyImage = 'caddy:2-alpine';
export const watchtowerImage = 'containrrr/watchtower';
export const botasaurusImage = 'html2rss/botasaurus-scrape-api:latest';
Loading