Crumb is AGPL-3.0, and self-hosting it for your own company is fully permitted. There's no Crumb account to create, and the same repo powers Community and Cloud. If you want a self-hosted feedback board that also covers the rest of the loop (an in-app widget, an inbox you can rank by revenue, a public roadmap, and customer notifications), here's the path from an empty server to a running install, plus what keeps it healthy afterwards.
Prerequisites
- A Linux server or VM. The repo's deploy guide uses Ubuntu 24.04 or 22.04 on a small VM (1 vCPU, 4 GB of RAM, about 50 GB of disk) and calls that plenty for one company.
- Docker Engine with the Compose plugin. Follow Docker's install guide. Its convenience script is quick, but Docker doesn't recommend it for production.
- A public hostname if customers will use the widget, since the widget script and API are served from your Crumb host.
- Optional: an SMTP relay for email, and OAuth apps for the integrations you want.
Crumb is a Node.js server app that talks to Postgres. It needs a VPS or container host; it won't run on shared web hosting or on Cloudflare Pages or Workers.
Install with Docker Compose
Clone the repo and create your environment file:
git clone https://github.com/vladharl/crumb.git crumb && cd crumb
cp .env.example .env
Open .env and set at least these four:
POSTGRES_PASSWORD: a long random string, e.g. fromopenssl rand -hex 24.CRUMB_APP_URL: the public URL you'll serve Crumb from.CRUMB_ENCRYPTION_KEY: fromopenssl rand -hex 32. Encrypts integration tokens at rest.CRUMB_INTERNAL_SWEEP_SECRET: fromopenssl rand -hex 32. Protects the maintenance endpoints.
Then bring up the whole stack (dashboard, widget, and Postgres):
docker compose up -d --build
The first build compiles the community image and takes a few minutes. Compose starts Postgres, waits until it's healthy, then starts the dashboard, which applies database migrations on every startup. Check that it all came up:
docker compose ps # all "healthy"/"running"
docker compose logs -f dashboard # watch boot + migrations
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3000/api/health/ready
Just evaluating on a laptop? The README's local dev setup seeds a sample workspace instead.
Make it reachable
The compose file binds Postgres and the dashboard to 127.0.0.1 by default, so nothing is exposed on the server's public IP until you choose how. There are two ways.
The built-in Cloudflare Tunnel. The compose file includes an optional cloudflared service behind a tunnel profile. Create a tunnel in the Cloudflare Zero Trust dashboard, add a public hostname that points to http://dashboard:3000, put the tunnel token in .env as CLOUDFLARE_TUNNEL_TOKEN, and start with the profile enabled:
docker compose --profile tunnel up -d --build
Cloudflare Tunnel connects outbound and doesn't need a publicly routable IP, so you can keep inbound ports 80 and 443 closed and allow only SSH.
Your own reverse proxy or load balancer, pointed at 127.0.0.1:3000 on the same host. Either way, set CRUMB_APP_URL to the public origin, because Crumb builds every OAuth callback URL from it.
First sign-in and the widget
On a fresh install, open your Crumb URL and go to /onboard to create the first workspace and admin. The page needs a one-time setup token, so generate the link on the server first:
docker compose exec dashboard node packages/db/dist/cli.mjs setup-link
Open the link it prints, which works once and expires after 60 minutes, and fill in the form; you're signed in as the admin when it's done. After that, sign-in is by magic link, and until email is configured, the link is printed to the logs:
docker compose logs dashboard | grep -i "magic\|login\|http"
Then embed the widget in your product: one script tag that loads widget.js from your Crumb host. The widget docs cover identifying signed-in users so every request arrives tied to the right account.
Configuration essentials
Everything is configured in .env, and .env.example documents each variable. Beyond the four above, these matter most:
| Variable | What it does |
|---|---|
CRUMB_EMAIL_PROVIDER, SMTP_*, CRUMB_EMAIL_FROM | Real email for magic links and customer notifications. Set the provider to smtp and add your relay's host, port, and credentials. |
CRUMB_STORAGE_PROVIDER | Set to postgres to keep attachments in the database. The default writes them to the container's disk, which is lost when the container is recreated. |
CRUMB_INBOUND_DOMAIN, CRUMB_INBOUND_SECRET | Lets customers reply to notification emails, and turns forwarded email into feedback to triage. |
SLACK_*, LINEAR_*, JIRA_*, GITHUB_*, HUBSPOT_*, SALESFORCE_* | Credentials for your own OAuth apps, one set per provider you connect. |
Register {CRUMB_APP_URL}/api/integrations/<provider>/callback as each provider's redirect URL. The SMTP relay can be Postmark, SendGrid, SES, Mailgun, or your own Postfix; set up SPF and DKIM for the sender domain first, or messages won't deliver. After editing .env, run docker compose up -d again to apply it. If you'd rather not keep secrets in .env, every sensitive variable also accepts a *_FILE companion for Docker secrets; the README shows how.
Backups and restore
Everything lives in a Postgres you control, so one pg_dump is a full backup (with attachments stored in Postgres, as above):
# Back up (compressed custom format)
docker compose exec -T postgres pg_dump -U crumb -Fc crumb > crumb-$(date +%F).dump
# Restore into a fresh DB
docker compose exec -T postgres pg_restore -U crumb -d crumb --clean --if-exists < crumb-2026-01-01.dump
Postgres's custom format is compressed and made for pg_restore, and pg_dump takes a consistent copy without blocking the app. Schedule it with host cron or a sidecar container, and ship the file off the server: a backup on the same disk isn't much of a backup.
Maintenance jobs
Two cron jobs keep things tidy. The sweep prunes orphaned uploads and usage events past their retention window (180 days by default). The CRM refresh only matters if you connected HubSpot or Salesforce.
# daily at 03:00
0 3 * * * curl -fsS -X POST -H "X-Crumb-Sweep-Secret: $CRUMB_INTERNAL_SWEEP_SECRET" http://127.0.0.1:3000/api/v1/internal/replay-sweep
# every 6 hours
0 */6 * * * curl -fsS -X POST -H "X-Crumb-Sweep-Secret: $CRUMB_INTERNAL_SWEEP_SECRET" http://127.0.0.1:3000/api/v1/internal/crm-sync
Make sure CRUMB_INTERNAL_SWEEP_SECRET is set in the crontab's environment, or the requests go out without the secret.
Upgrades
Take a backup, skim the changelog, then pull and rebuild:
git pull && docker compose --profile tunnel up -d --build
Drop --profile tunnel if you don't use the tunnel. Migrations run automatically when the new dashboard starts, and they only go forward, so the way back from a bad upgrade is the previous release plus the dump you took first. On a low-memory server you can skip building: the repo's release workflow publishes the community image to GitHub's container registry, and the deploy guide shows how to pull it instead.
Health checks
Two endpoints are built in. GET /api/health is liveness: it returns 200 while the process is up, so restart the container if it fails. GET /api/health/ready is readiness: 200 only when the database is reachable and 503 otherwise, so route traffic only to ready instances. Handy behind a load balancer. The bundled compose file already uses the readiness check as the dashboard's container healthcheck, which is what docker compose ps reports.
When to choose Crumb Cloud instead
Self-hosting gives you the whole core loop. A few features are Cloud-only by design:
| Capability | Self-host (free) | Crumb Cloud |
|---|---|---|
| Widget, inbox, status flow, public roadmap | Yes | Yes, on every plan |
| Email delivery | Your SMTP relay | Managed, on every plan |
| Slack, Linear, Jira, GitHub, HubSpot, Salesforce | Your own OAuth apps | One click, Team and up |
| Usage analytics | Always on | Team and up |
| AI clustering, ticket and reply drafts, Ask your feedback | No | Team and up |
| Session replay | No | Growth and up |
| Running it | You | We do |
Self-host if your data has to stay on your infrastructure and you're comfortable running Docker and Postgres. Choose Crumb Cloud if you want the AI features or session replay, or would rather not register OAuth apps and run backups: it's free to start, and Team is $19/mo billed annually, flat per workspace. Same software either way.
Frequently asked questions
Is Crumb free to self-host?
Yes. Crumb is AGPL-3.0 and free to self-host for your own company, with no Crumb account required. If you offer a modified Crumb to other organizations as a service, the AGPL requires you to make your fork's source available to them.
What do I need to self-host Crumb?
A Linux server or VM with Docker and Docker Compose. The deploy guide uses a small VM with 1 vCPU, 4 GB of RAM, and about 50 GB of disk for one company. Add an SMTP relay for email, and a public hostname if customers will use the widget.
How do I back up a self-hosted Crumb?
Run docker compose exec -T postgres pg_dump -U crumb -Fc crumb > crumb-$(date +%F).dump on a schedule and copy the file off the server. Set CRUMB_STORAGE_PROVIDER=postgres so attachments are included in the dump.
Does self-hosted Crumb include the AI features?
No. AI clustering, ticket drafting, Ask your feedback, and session replay are Crumb Cloud features. The core loop works self-hosted: the widget, inbox, status flow, customer notifications, public roadmap, integrations through your own OAuth apps, usage analytics, the API, and the MCP server.
How do I upgrade a self-hosted Crumb?
Back up first, then run git pull && docker compose --profile tunnel up -d --build, leaving out --profile tunnel if you don't use the Cloudflare Tunnel. Database migrations run automatically when the dashboard starts.