Cloudflare Deployment
Step-by-step guide to deploying LinkDen on Cloudflare Workers and D1.
Cloudflare Deployment
LinkDen is designed to run entirely on the Cloudflare stack. This guide walks through the complete deployment process, from account setup to a live, production-ready instance.
Architecture Overview
LinkDen deploys two Cloudflare Workers and a D1 database:
┌──────────────────────────┐ ┌──────────────────────┐ ┌──────────────┐
│ Cloudflare Worker │ │ Cloudflare Worker │ │ Cloudflare D1│
│ linkden-web │────▶│ linkden-api │────▶│ (SQLite DB) │
│ (Next.js via OpenNext) │ │ (Hono API + tRPC) │ │ │
└──────────────────────────┘ └──────────────────────┘ └──────────────┘| Service | Purpose | Free Tier |
|---|---|---|
| linkden-web Worker | Next.js frontend (SSR + static assets via OpenNext) | 100,000 requests/day |
| linkden-api Worker | Hono API server with tRPC | 100,000 requests/day |
| Cloudflare D1 | SQLite database for all application data | 5M rows read/day, 100K writes/day |
The web frontend is deployed using @opennextjs/cloudflare, which compiles the full Next.js app (including dynamic routes and SSR) into a Cloudflare Worker.
The free tier is sufficient for most single-user instances.
Prerequisites
Before you begin, make sure you have the following:
- A Cloudflare account (free tier is sufficient).
- Node.js 20+ installed on your machine.
- pnpm 9+ installed globally (
npm install -g pnpm). - The LinkDen repository cloned locally:
git clone https://github.com/mrdemonwolf/linkden.git
cd LinkDen
pnpm installStep 1: Create a Cloudflare API Token
The API token allows LinkDen's deployment tooling (Alchemy + Wrangler) to manage Cloudflare resources on your behalf.
- Log in to the Cloudflare dashboard.
- Click your profile icon in the top-right corner.
- Go to My Profile > API Tokens.
- Click Create Token.
- Select the Custom token template.
- Configure the following permissions:
| Permission Scope | Resource | Access Level |
|---|---|---|
| Account | Cloudflare Workers Scripts | Edit |
| Account | D1 | Edit |
| Account | Workers KV Storage | Edit |
- Under Account Resources, select your account.
- Click Continue to summary, then Create Token.
- Copy the token immediately. It is shown only once. Store it securely.
Step 2: Find Your Account ID
- Go to the Cloudflare dashboard overview page.
- Your Account ID is displayed in the right sidebar under Account details.
- Copy it.
Alternatively, use the Wrangler CLI:
npx wrangler whoamiStep 3: Install and Authenticate Wrangler CLI
Wrangler is Cloudflare's CLI tool. Authenticate with your Cloudflare account:
wrangler loginThis opens a browser window for OAuth authentication. To verify:
wrangler whoamiStep 4: Configure Environment Variables
Create a .env file in the project root:
# Cloudflare credentials
CLOUDFLARE_API_TOKEN=your-api-token-here
CLOUDFLARE_ACCOUNT_ID=your-account-id-here
# App URLs (update after first deploy with your actual worker URLs)
NEXT_PUBLIC_SITE_URL=https://linkden-web.<your-subdomain>.workers.dev
NEXT_PUBLIC_API_URL=https://linkden-api.<your-subdomain>.workers.dev
APP_URL=https://linkden-api.<your-subdomain>.workers.dev
CORS_ORIGIN=https://linkden-web.<your-subdomain>.workers.dev
# Authentication (choose one or both)
CF_ACCESS_TEAM_DOMAIN=your-team-domain
# OR
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_your-key
CLERK_SECRET_KEY=sk_live_your-keySee the Environment Variables reference for a full list of all variables.
Step 5: Create the D1 Database and Push Schema
Create the D1 database:
npx wrangler d1 create linkden-dbCopy the database_id from the output and update apps/server/wrangler.toml:
[[d1_databases]]
binding = "DB"
database_name = "linkden-db"
database_id = "your-database-id-here"Then push the schema. You can use drizzle-kit with a CLOUDFLARE_API_TOKEN:
CLOUDFLARE_ACCOUNT_ID=your-account-id CLOUDFLARE_API_TOKEN=your-token pnpm db:pushOr push directly via wrangler (which uses your wrangler login session):
npx wrangler d1 execute linkden-db --remote --file=packages/db/drizzle/schema.sqlStep 6: Deploy
With everything configured, deploy the entire stack:
pnpm cf:deployThis command:
- Deploys the
linkden-apiWorker (Hono API + tRPC) with D1 bindings via wrangler. - Builds the Next.js app via
@opennextjs/cloudflare(OpenNext). - Deploys the
linkden-webWorker with static assets via wrangler.
The deploy output shows your live URLs:
API: https://linkden-api.<your-subdomain>.workers.dev
Web: https://linkden-web.<your-subdomain>.workers.devYou can also deploy each part independently:
# Deploy only the API worker
pnpm cf:deploy:api
# Deploy only the web frontend
pnpm cf:deploy:webStep 7: Set Up Authentication
LinkDen supports Cloudflare Access and/or Clerk for admin authentication. For Cloudflare deployments, Cloudflare Access is recommended — it protects your admin panel at the edge before requests reach your Worker.
Quick Cloudflare Access Setup
- Go to Zero Trust in the Cloudflare dashboard.
- Set up a team name (e.g.,
mycompany) if you have not already. - Go to Settings > Authentication and add an identity provider (One-time PIN is the simplest).
- Go to Access > Applications > Add an application > Self-hosted.
- Set the application domain to
linkden-web.<your-subdomain>.workers.devwith path/admin. - Create an access policy that allows only your email address.
- Set
CF_ACCESS_TEAM_DOMAINinapps/server/wrangler.toml:
[vars]
CF_ACCESS_TEAM_DOMAIN = "mycompany"Then redeploy: pnpm cf:deploy:api
See the Authentication guide for detailed instructions, Clerk setup, or using both providers together.
Step 8: Set Secrets (Optional)
If using Clerk or Resend, set their secret keys as encrypted secrets on the API Worker:
cd apps/server
echo "sk_live_your-key" | wrangler secret put CLERK_SECRET_KEY
echo "re_your-key" | wrangler secret put RESEND_API_KEYOr via the Cloudflare dashboard: Workers & Pages > linkden-api > Settings > Variables.
Step 9: Verify the Deployment
- Visit your web Worker URL. You should see the public link page.
- Navigate to
/adminand confirm the sign-in flow works. - Sign in and try creating a link to verify the API connection.
- Check the analytics dashboard.
If something is not working, check the Worker logs:
wrangler tail linkden-api
wrangler tail linkden-webStep 10: Set Up Custom Domains (Optional)
See the Custom Domain guide for detailed instructions.
To add custom domains to your Workers:
- Go to Workers & Pages in the Cloudflare dashboard.
- Click on your Worker (
linkden-weborlinkden-api). - Go to Settings > Domains & Routes.
- Add your custom domain.
After adding custom domains, update your environment variables:
NEXT_PUBLIC_SITE_URL=https://links.yourdomain.com
NEXT_PUBLIC_API_URL=https://api.yourdomain.com
APP_URL=https://api.yourdomain.com
CORS_ORIGIN=https://links.yourdomain.comThen redeploy: pnpm cf:deploy
Subsequent Deploys
After the initial setup, deploying updates is a single command:
git pull origin main
pnpm cf:deployAlchemy only updates resources that changed, making subsequent deploys faster.
SSL/TLS Configuration
Cloudflare provides SSL/TLS automatically for all Workers deployments:
- Default
*.workers.devsubdomains: Full SSL is enabled by default. - Custom domains on Cloudflare DNS: Cloudflare automatically provisions and renews SSL certificates.
No manual certificate management is required.
Monitoring and Logs
Real-Time Logs
Stream live logs from your Workers:
# API logs
wrangler tail linkden-api
# Web frontend logs
wrangler tail linkden-web
# Only errors
wrangler tail linkden-api --status errorCloudflare Dashboard Analytics
- Workers analytics: Workers & Pages > your Worker > Analytics.
- D1 analytics: Workers & Pages > D1 > your database.
Tearing Down
To remove all Cloudflare resources created by Alchemy:
pnpm cf:destroyThis deletes the API Worker and the D1 database. The web Worker must be deleted separately from the Cloudflare dashboard.
This action is irreversible. Back up your D1 database first:
wrangler d1 export linkden-db --output backup.sqlTroubleshooting
"Authentication error" on deploy
Your API token may have expired or lack the required permissions.
wrangler whoamiCORS errors in the browser console
The CORS_ORIGIN environment variable on the API Worker must exactly match the origin of your web app (including protocol, no trailing slash).
# Correct
CORS_ORIGIN=https://linkden-web.your-subdomain.workers.dev
# Wrong
CORS_ORIGIN=https://linkden-web.your-subdomain.workers.dev/Worker returns 1101 error
Check the Worker logs:
wrangler tail linkden-api --status errorCommon causes: missing environment variable, D1 binding not configured, auth key mismatch.
Cost Considerations
For a typical single-user link page:
| Metric | Typical Usage | Free Tier Limit |
|---|---|---|
| Worker requests (both) | ~10K/month | 100,000/day each |
| D1 rows read | ~300/day | 5,000,000/day |
| D1 rows written | ~50/day | 100,000/day |
| D1 storage | < 50 MB | 5 GB |
Most LinkDen instances will operate entirely within the free tier indefinitely.