Host it yourself, in three commands.
Kytelink is open source, ground up. Nothing is closed, and every third-party service has a drop-in open-source or self-hostable equivalent. Run it on your own box, or use ours — either way it costs nothing.
Every hosted piece has an open swap-in.
The founder's hosted config is one example configuration, not a requirement. The app code never assumes a provider — it only speaks standard protocols: the Postgres wire protocol, Redis, the S3 API, SMTP, and OpenAI's HTTP shape.
| Hosted stack | Self-hosted equivalent |
|---|---|
| Cloudflare R2 | MinIO, or any S3-compatible bucket |
| Resend | SMTP (any provider, or your own Postfix) |
| ClickHouse Cloud | ClickHouse OSS (a VM, or the compose service) |
| Neon (Postgres) | Plain Postgres (compose, RDS, your own box) |
| OpenAI | Any OpenAI-compatible endpoint, incl. local models |
| Vercel domain automation | Manual DNS |
Three commands to a live stack.
One interactive wizard handles the whole first boot. Postgres and Redis are the only hard requirement — it runs them in Docker for you. Analytics, uploads, and a local email inbox are each one y/n question; skip any of them and that feature simply switches off gracefully.
Change your mind later by re-running the wizard or editing .env — and if you run pnpm dev before setting up, it stops with the exact command to run instead of crashing.
Wire up what you need.
The primary datastore: users, kytes, memberships, invites, assets, domains, moderation, schedules. Any Postgres 16+ works.
Caches, rate limits, BullMQ queues, and the beacon buffer. Any Redis 7+ (or protocol-compatible store).
Backs page, link, and product analytics and their rollups. Skip it entirely and the app runs with analytics off.
Owned static assets and user uploads share one bucket. Any S3 API works: MinIO locally, Cloudflare R2, or AWS S3.
Three backends via EMAIL_PROVIDER: console (stdout), smtp (mailpit locally, Postfix in prod), or resend.
MODERATION_PROVIDER=none auto-approves everything. Set openai to enable moderation and the AI link importer.
DOMAIN_PROVIDER=proxy verifies by DNS and lets your reverse proxy issue certificates on demand — point CUSTOM_DOMAIN_A_RECORD/CNAME_TARGET at your edge and run the shipped Caddy config. On Vercel, set vercel with an API token instead.
What turns off, gracefully.
A disabled capability always means the UI surface is absent or replaced with one calm card — never a broken button. Its tRPC procedures return a typed FEATURE_DISABLED error as the backstop.
| Missing | Off | What the user sees |
|---|---|---|
| CLICKHOUSE_URL | analytics | Beacon endpoints return 202 and drop; no Analytics tab in the editor; admin shows a calm “Analytics is off” card. Everything else works. |
| AWS_* (S3) | uploads | Upload tiles hidden; onboarding offers built-in default avatars; OG cards fall back to text-only. Owned static assets still serve. |
| RESEND_API_KEY / SMTP | emailDelivery | OTPs and invites print to server stdout. Set EMAIL_PROVIDER=smtp against mailpit to read them in an inbox instead. |
| OPENAI_API_KEY | moderation | Publishes auto-approve and the AI “Other” importer hides. Linktree/Beacons/Bio.link imports still work (deterministic parsers). |
| GOOGLE_* / GITHUB_* | that OAuth button | The button is hidden from the auth screen; email OTP always works. |
| VERCEL_* | domains | The custom-domain UI shows DNS instructions instead of automated verification. |
Before you go live.
apps/web's Next.js ISR cache should live on a persistent volume in production, or page revalidation resets on every deploy.
Set PROCESS_ROLE=worker on a dedicated process for the BullMQ workers — don't run them inline with the process serving HTTP traffic.
Postgres is the can't-lose store. Use pg_dump on a schedule, or point-in-time recovery if your provider offers it.
The S3 bucket holds irreplaceable user uploads. Periodic sync or cross-region replication is recommended. ClickHouse loss is accepted as lossy.
Read every line, then run it.
The full guide, docker-compose file, and env reference live in the repo. It's yours.
View on GitHub