Complete guide to deploying your own Matrix homeserver on Cloudflare Workers.
The fastest way to deploy is using the Deploy to Cloudflare button:
When you click the button, Cloudflare will:
- Fork the repository to your GitHub/GitLab account
- Provision resources automatically:
- D1 database
- All KV namespaces (SESSIONS, DEVICE_KEYS, CACHE, etc.)
- R2 bucket for media storage
- Durable Objects
- Workflows
- Deploy the Worker to your Cloudflare account
- Set up Workers Builds for continuous deployment from your forked repo
You still need to complete these steps manually:
The SERVER_NAME environment variable must match your domain. Update it in your Cloudflare dashboard:
- Go to Workers & Pages
- Select your deployed Worker
- Go to Settings → Variables and Secrets
- Edit
SERVER_NAMEto your domain (e.g.,matrix.yourdomain.com) - Click Deploy to apply changes
Important: SERVER_NAME cannot be changed after users register. Choose carefully.
The D1 database is created but empty. You must run all migrations:
- Find your D1 database name in the Worker settings (under D1 Database Bindings)
- Run each migration (replace
YOUR_DB_NAMEwith your actual database name):
# Clone your forked repository locally
git clone https://github.com/YOUR_USERNAME/matrix-workers
cd matrix-workers
# Authenticate wrangler
npx wrangler login
# Run all migrations in order
npx wrangler d1 execute YOUR_DB_NAME --remote --file=migrations/schema.sql
npx wrangler d1 execute YOUR_DB_NAME --remote --file=migrations/002_phase1_e2ee.sql
npx wrangler d1 execute YOUR_DB_NAME --remote --file=migrations/003_account_management.sql
npx wrangler d1 execute YOUR_DB_NAME --remote --file=migrations/004_reports_and_notices.sql
# Note: Two migrations share the 005 prefix (both must be run)
npx wrangler d1 execute YOUR_DB_NAME --remote --file=migrations/005_server_config.sql
npx wrangler d1 execute YOUR_DB_NAME --remote --file=migrations/005_idp_providers.sql
npx wrangler d1 execute YOUR_DB_NAME --remote --file=migrations/006_query_optimization.sql
npx wrangler d1 execute YOUR_DB_NAME --remote --file=migrations/007_secure_server_keys.sql
npx wrangler d1 execute YOUR_DB_NAME --remote --file=migrations/008_federation_transactions.sqlYour Worker is deployed at *.workers.dev but Matrix federation requires a proper domain:
- Go to your Worker in the dashboard
- Navigate to Settings → Domains & Routes
- Click Add → Custom Domain
- Enter your domain (e.g.,
matrix.yourdomain.com) - Cloudflare automatically configures DNS if your domain is on Cloudflare
Test your deployment:
# Replace with your domain
curl https://matrix.yourdomain.com/_matrix/client/versions
# Check federation
curl https://matrix.yourdomain.com/_matrix/federation/v1/versionRun the Federation Tester with your server name.
curl -X POST "https://matrix.yourdomain.com/_matrix/client/v3/register" \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"password": "your-secure-password",
"auth": {"type": "m.login.dummy"}
}'For more control, deploy manually using the steps below.
-
Cloudflare Account with Workers Paid plan ($5/month)
- Required for Durable Objects, which are essential for real-time sync
- Sign up at cloudflare.com
-
Node.js 18+
node --version # Should be v18.0.0 or higher -
Wrangler CLI
npm install -g wrangler wrangler --version
-
Authenticate Wrangler
npx wrangler login
This opens a browser to authenticate with your Cloudflare account.
-
A Domain managed by Cloudflare (for federation to work)
- Matrix federation requires a proper domain name
- The domain's DNS must be managed by Cloudflare
git clone https://github.com/nkuntz1934/matrix-workers
cd matrix-workers
npm installRun these commands and save the output - you'll need the IDs for configuration.
npx wrangler whoamiNote your Account ID from the output.
npx wrangler d1 create my-matrix-dbOutput will include:
Created D1 database 'my-matrix-db'
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
Save the database_id.
Create all 6 required KV namespaces:
npx wrangler kv namespace create SESSIONS
npx wrangler kv namespace create DEVICE_KEYS
npx wrangler kv namespace create CACHE
npx wrangler kv namespace create CROSS_SIGNING_KEYS
npx wrangler kv namespace create ACCOUNT_DATA
npx wrangler kv namespace create ONE_TIME_KEYSEach command outputs an ID. Save all 6 IDs.
Example output:
Add the following to your wrangler configuration file:
kv_namespaces = [
{ binding = "SESSIONS", id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
]
npx wrangler r2 bucket create my-matrix-mediaSave the bucket name (you chose it, so just remember it).
Open wrangler.jsonc and replace all placeholder values with your actual IDs.
"d1_databases": [
{
"binding": "DB",
"database_name": "my-matrix-db", // Name you chose
"database_id": "YOUR_DATABASE_ID" // From d1 create output
}
]"kv_namespaces": [
{ "binding": "SESSIONS", "id": "YOUR_SESSIONS_KV_ID" },
{ "binding": "DEVICE_KEYS", "id": "YOUR_DEVICE_KEYS_KV_ID" },
{ "binding": "CACHE", "id": "YOUR_CACHE_KV_ID" },
{ "binding": "CROSS_SIGNING_KEYS", "id": "YOUR_CROSS_SIGNING_KEYS_KV_ID" },
{ "binding": "ACCOUNT_DATA", "id": "YOUR_ACCOUNT_DATA_KV_ID" },
{ "binding": "ONE_TIME_KEYS", "id": "YOUR_ONE_TIME_KEYS_KV_ID" }
]"r2_buckets": [
{
"binding": "MEDIA",
"bucket_name": "my-matrix-media" // Name you chose
}
]"vars": {
"SERVER_NAME": "matrix.yourdomain.com", // Your Matrix server domain
"SERVER_VERSION": "0.1.0"
}Important: SERVER_NAME must match the domain you'll use for Matrix. This cannot be changed after users register.
"routes": [
{
"pattern": "matrix.yourdomain.com",
"custom_domain": true
}
]If you're not using LiveKit for video calls, remove or comment out:
// Remove these sections if not using LiveKit:
"vpc_services": [ ... ],
"vars": {
// Remove these:
"LIVEKIT_API_KEY": "...",
"LIVEKIT_URL": "..."
}Apply all migrations to your D1 database:
# Replace 'my-matrix-db' with your actual database name
npx wrangler d1 execute my-matrix-db --remote --file=migrations/schema.sql
npx wrangler d1 execute my-matrix-db --remote --file=migrations/002_phase1_e2ee.sql
npx wrangler d1 execute my-matrix-db --remote --file=migrations/003_account_management.sql
npx wrangler d1 execute my-matrix-db --remote --file=migrations/004_reports_and_notices.sql
# Note: Two migrations share the 005 prefix (both must be run)
npx wrangler d1 execute my-matrix-db --remote --file=migrations/005_server_config.sql
npx wrangler d1 execute my-matrix-db --remote --file=migrations/005_idp_providers.sql
npx wrangler d1 execute my-matrix-db --remote --file=migrations/006_query_optimization.sql
npx wrangler d1 execute my-matrix-db --remote --file=migrations/007_secure_server_keys.sql
npx wrangler d1 execute my-matrix-db --remote --file=migrations/008_federation_transactions.sqlEach migration should complete with "success": true.
npm run deployOr directly:
npx wrangler deployThe output will show your worker URL (e.g., my-matrix-server.your-subdomain.workers.dev).
- Go to Cloudflare Dashboard
- Navigate to Workers & Pages → Your Worker → Settings → Domains & Routes
- Click Add → Custom Domain
- Enter your domain (e.g.,
matrix.yourdomain.com) - Cloudflare automatically configures DNS
If using manual DNS, add these records:
| Type | Name | Content | Proxy |
|---|---|---|---|
| CNAME | matrix | your-worker.workers.dev | Proxied |
Matrix clients and servers need .well-known endpoints. These are automatically served by the worker at:
https://matrix.yourdomain.com/.well-known/matrix/serverhttps://matrix.yourdomain.com/.well-known/matrix/client
For full federation support, ensure your domain resolves correctly. The worker handles the .well-known responses automatically.
# Replace with your domain
export MATRIX_SERVER="https://matrix.yourdomain.com"
# Check server is responding
curl -s "$MATRIX_SERVER/_matrix/client/versions" | jq .
# Check well-known endpoints
curl -s "$MATRIX_SERVER/.well-known/matrix/server" | jq .
curl -s "$MATRIX_SERVER/.well-known/matrix/client" | jq .
# Check federation keys
curl -s "$MATRIX_SERVER/_matrix/key/v2/server" | jq .
# Check federation version
curl -s "$MATRIX_SERVER/_matrix/federation/v1/version" | jq .Visit the Matrix Federation Tester:
https://federationtester.matrix.org/api/report?server_name=matrix.yourdomain.com
Look for "FederationOK": true in the response.
curl -X POST "$MATRIX_SERVER/_matrix/client/v3/register" \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"password": "your-secure-password",
"auth": {
"type": "m.login.dummy"
}
}'- Open Element Web
- Click Sign In → Edit homeserver
- Enter your server URL:
https://matrix.yourdomain.com - Sign in with your registered user
Cloudflare provides TURN servers. To enable:
- Go to Cloudflare Dashboard → Calls → TURN
- Create a TURN key
- Add to
wrangler.jsonc:"vars": { "TURN_KEY_ID": "your-turn-key-id" }
- Set the secret:
npx wrangler secret put TURN_API_TOKEN # Paste your TURN API token when prompted
If you have a LiveKit server:
- Add to
wrangler.jsonc:"vars": { "LIVEKIT_API_KEY": "your-api-key", "LIVEKIT_URL": "wss://your-livekit-server.com" }
- Set the secret:
npx wrangler secret put LIVEKIT_API_SECRET
For direct Apple Push Notification support:
npx wrangler secret put APNS_KEY_ID # From Apple Developer Portal
npx wrangler secret put APNS_TEAM_ID # Your Apple Team ID
npx wrangler secret put APNS_PRIVATE_KEY # Contents of .p8 fileFor OpenID Connect login:
npx wrangler secret put OIDC_ENCRYPTION_KEY
# Generate with: openssl rand -base64 32Durable Objects require the Workers Paid plan ($5/month). Upgrade at: Cloudflare Dashboard → Workers & Pages → Plans
Ensure you've run all migrations and the database name in wrangler.jsonc matches what you created.
- Verify your domain's DNS is managed by Cloudflare
- Check
.well-known/matrix/serverreturns correct content - Ensure the worker is deployed and responding
- Check the signing key is generated (first request auto-generates it)
Check Cloudflare Workers logs:
npx wrangler tailRegistration is enabled by default. If you've disabled it and need to create an admin:
# Connect to D1 directly
npx wrangler d1 execute my-matrix-db --remote --command "SELECT * FROM users LIMIT 5"The server has rate limiting. Default limits:
- Login: 10 requests/minute
- Register: 5 requests/minute
- General API: 100 requests/minute
To update your deployment:
git pull
npm install
npm run deployIf there are new migrations, run them before deploying:
npx wrangler d1 execute my-matrix-db --remote --file=migrations/NEW_MIGRATION.sqlYour deployed Matrix server uses:
| Component | Cloudflare Service | Purpose |
|---|---|---|
| API & Routing | Workers | HTTP request handling |
| Database | D1 | Users, rooms, events, messages |
| Sessions | KV | Access tokens, fast lookups |
| E2EE Keys | KV | Device keys, cross-signing |
| Media | R2 | Images, files, avatars |
| Real-time Sync | Durable Objects | Live updates, typing indicators |
| Federation | Durable Objects | Server-to-server communication |
| Background Jobs | Workflows | Room joins, push notifications |
- Issues: GitHub Issues
- Matrix Spec: spec.matrix.org
- Cloudflare Docs: developers.cloudflare.com
{ "name": "my-matrix-server", // Your worker name "account_id": "YOUR_ACCOUNT_ID", // From npx wrangler whoami // ... rest of config }