README zhiyingzzhou/renewlet
Renewlet
Renewlet is a self-hosted subscription ledger for tracking recurring charges and sending renewal reminders.
It records renewal dates, prices, currencies, categories, payment methods, logos, budgets, notes, and notification settings. It can run as a single Docker container, or on Cloudflare Workers with D1, R2, and Cron Triggers.
Demo
Try the live demo: https://renewlet-demo.olyq.org/
Sign in with demo@renewlet.local / renewlet-demo. The demo resets regularly, so please do not put real personal data or credentials there.
Features
- Subscription records with billing cycles, statuses, tags, websites, notes, logos, categories, and payment methods.
- Reminder jobs based on each user's IANA time zone, local notification time, reminder days, repeat reminders, delivery history, and failed-send retries.
- Notifications through Telegram, Notifyx, Webhook, WeCom Bot, SMTP email, Bark, ServerChan, Discord, and PushPlus.
- Account security with authenticator codes, one-time recovery codes, and passkey sign-in.
- Monthly and yearly cost normalization, budget usage, category charts, payment-method charts, and inactive-subscription savings.
- AI recognition for bill screenshots, notes, CSV/TSV, and pasted table text; drafts are reviewed before import.
- Global private ICS feed and per-subscription calendar feeds.
- Public subscription status pages with per-subscription visibility and optional price display.
- Import and export Renewlet data, plus Wallos file imports.
- Uploaded logos, image URLs, built-in icon sources, and favicon fallback suggestions.
- Docker deployment with React, Go/PocketBase, SQLite, and static assets in one container.
- Cloudflare Workers deployment with React static assets, Worker API, D1, R2, and Cron Triggers.
- Mobile web views for subscriptions, filters, statistics, calendar, and settings.
Docker Quick Start
Requirements: Docker and Docker Compose v2.
mkdir -p renewlet && cd renewlet
curl -fsSL https://raw.githubusercontent.com/zhiyingzzhou/renewlet/main/deploy/docker-deploy.sh | bash
docker compose up -dOpen:
http://localhost:3000/setup
The deploy script creates docker-compose.yml, .env, and data/, then writes PB_ENCRYPTION_KEY and CRON_SECRET.
For production, pin a stable image tag:
sed -i.bak 's#RENEWLET_IMAGE=.*#RENEWLET_IMAGE="zhiyingzzhou/renewlet:0.2.3"#' .env
docker compose pull
docker compose up -dIf Docker Hub is unavailable, use GHCR:
RENEWLET_IMAGE="ghcr.io/zhiyingzzhou/renewlet:0.2.3"Cloudflare Workers
Use the deploy button for a Cloudflare-managed repository, or follow Cloudflare Workers deploy to manage D1, R2, GitHub Actions, and secrets yourself.
Do not click the deploy button again to upgrade. One-click deploy users run Sync Renewlet Upstream in the generated repository connected by Cloudflare Builds; manual deploy users update their fork to the latest Renewlet version, then run Cloudflare Worker. Cloudflare updates must apply D1 migrations before publishing the Worker.
Upgrade
Back up data and config before upgrading:
tar -czf renewlet-backup-$(date +%F).tgz .env docker-compose.yml dataUpgrade with Docker Compose:
sed -i.bak 's#RENEWLET_IMAGE=.*#RENEWLET_IMAGE="zhiyingzzhou/renewlet:0.2.3"#' .env
docker compose pull
docker compose up -d
docker compose logs -fDocker release images with the current binary layout can also update from the version badge at the top of Renewlet. Older images must run docker compose pull && docker compose up -d once before in-app updates become available.
Common Commands
docker compose ps
docker compose logs -f
docker compose downCommon .env values:
| Variable | Purpose |
|---|---|
PORT |
Public port, 3000 by default. |
RENEWLET_IMAGE |
Docker image, zhiyingzzhou/renewlet:latest by default. |
TZ |
Container time zone for logs. Reminder times use each user's time zone. |
PB_ENCRYPTION_KEY |
Encryption key for sensitive PocketBase settings. Do not rotate it casually after deployment. |
CRON_SECRET |
Bearer secret for external Cron calls to /api/cron/notifications. |
RENEWLET_DEMO_MODE |
Docker Demo Mode switch, false by default. |
RENEWLET_CUSTOM_HEAD_SCRIPT |
Optional deployer-provided external <script> injection. Empty by default; leave unset to inject no external script. |
NOTIFICATION_SCHEDULER_ENABLED |
Built-in notification scheduler switch, true by default. |
The full Docker environment template is in .env.example.
Custom Head Script
Renewlet does not inject external scripts by default. When RENEWLET_CUSTOM_HEAD_SCRIPT is set, Renewlet injects exactly one deployer-provided external <script> tag into the SPA <head>:
RENEWLET_CUSTOM_HEAD_SCRIPT='<script defer src="https://cdn.example.com/widget.js" data-host-url="https://api.example.com/widget"></script>'Renewlet accepts only a single external script tag with src and no inline content. The script origin is automatically added to script-src and connect-src; when data-host-url is present, its origin is also added to connect-src.
Docker/Go deployments inject this at runtime, so changing the environment variable only requires restarting Renewlet. Cloudflare Static Assets reads the variable at build time, so changes require rebuilding and redeploying.
Screenshots
AI recognition
|
Public subscription status page
|
Subscriptions
|
Statistics
|
Renewal calendar
|
Notifications
|
Mobile
Mobile subscriptions
|
Mobile notification methods
|
Contributing
Issues, documentation fixes, tests, and pull requests are welcome. For larger changes, open an issue first with the goal, use case, and rough approach.
License
Renewlet is open-sourced under the MIT License.








