---
title: "Deploy EmDash to Cloudflare in 10 Minutes"
description: "Step-by-step deployment of an EmDash site to Cloudflare Pages, Workers, D1, and R2. From local development to a live production URL."
article_type: sub-pillar
canonical: https://dashstro.com/learn/deploy-emdash-cloudflare
---
:::bluf
Create a D1 database and R2 bucket, wire them into `wrangler.jsonc` with the right binding names (`DB` and `MEDIA`), connect your repo to Cloudflare Pages, and push. First deploy takes about 10 minutes, including the setup wizard on the live admin URL. After that, every `git push` auto-deploys — no server to restart, no cache to clear.
:::

You have an EmDash site running locally. Now you want it live on the internet. Cloudflare provides everything EmDash needs — compute (Workers), database (D1), media storage (R2), and hosting (Pages). Here's how to set it all up. I deployed dashtro.com this way and it took less than 10 minutes.

## Prerequisites

- A working EmDash site running locally (see Build Your First EmDash Website)
- A Cloudflare account (free tier works)
- Your site in a Git repository (GitHub, GitLab, or Bitbucket)
- Wrangler CLI installed: npm install -g wrangler

## Step 1: Create a D1 database

Log into your Cloudflare dashboard. Navigate to Workers & Pages > D1 > Create database. Give it a name like my-site-db. Copy the database ID — you'll need it for the wrangler config.

Or use the CLI:

```bash filename=Terminal
wrangler d1 create my-site-db
```

It prints the database ID directly.

## Step 2: Create an R2 bucket

In the dashboard: R2 > Create bucket. Name it something like my-site-media. The bucket name must be globally unique.

Or CLI:

```bash filename=Terminal
wrangler r2 bucket create my-site-media
```

## Step 3: Update wrangler.jsonc

Open wrangler.jsonc in your project root. Add the D1 and R2 bindings:

Set the d1_databases binding name to "DB" with your database ID and name. Set the r2_buckets binding name to "MEDIA" with your bucket name. These names must match what's in your astro.config.mjs — the d1({ binding: "DB" }) and r2({ binding: "MEDIA" }) calls.

Also enable nodejs_compat in the compatibility_flags array and set compatibility_date to today's date. EmDash requires Node.js compatibility mode in the Workers runtime.

## Step 4: Connect to Cloudflare Pages

In the Cloudflare dashboard: Workers & Pages > Create application > Pages > Connect to Git. Select your repository and branch.

For build settings, set the framework preset to Astro. The build command is npx astro build and the output directory is dist. Cloudflare Pages handles the rest.

| Step | Command / Action | What it does |
| --- | --- | --- |
| Create D1 database | wrangler d1 create my-site-db | Provisions a serverless SQLite database on Cloudflare |
| Create R2 bucket | wrangler r2 bucket create my-site-media | Creates object storage for media uploads |
| Update wrangler.jsonc | Add d1_databases and r2_buckets bindings | Connects your code to D1 and R2 in production |
| Connect to Pages | Workers & Pages > Create application > Pages | Links your Git repo for auto-deploy on push |
| Bind D1 and R2 | Pages project settings > Functions | Activates database and media storage for the deployed site |
| Push and verify | git push | Triggers build; visit /_emdash/admin to complete setup |

## Step 5: Bind D1 and R2 to your Pages project

After the first deploy, go to your Pages project settings. Under Functions > D1 database bindings, add a binding with variable name DB pointing to your D1 database. Under R2 bucket bindings, add MEDIA pointing to your R2 bucket.

These bindings connect your deployed site to the database and media storage you created. Without them, your site will build but fail at runtime with binding errors.

## Step 6: Deploy and verify

Push a commit to trigger a new deploy. Cloudflare Pages builds your site and deploys it to a .pages.dev URL. Once live, visit your site and check that pages load. Then visit /_emdash/admin to run the setup wizard and create your admin account.

Important: the first time you access the admin on production, EmDash runs its database migrations and seeds. This creates all your content type tables, taxonomy definitions, and any seed content. It only happens once.

:::pullquote
The entire deploy process is 'push to main.' Cloudflare Pages builds your site automatically on every push.
:::

## Custom domain

In your Pages project settings, go to Custom domains and add your domain. If your domain is already on Cloudflare (recommended), it configures DNS automatically. If it's elsewhere, you'll need to add a CNAME record pointing to your .pages.dev URL.

## What happens on each deploy

After initial setup, every git push triggers a build. Cloudflare Pages runs npx astro build, deploys the output to its edge network, and your site is live globally in under two minutes. There's no server to restart, no cache to clear, no deployment scripts to maintain.

Your content (in D1) and media (in R2) persist across deploys. Only your site code and templates change. Content editors can keep publishing through the admin panel while you push code updates without interference.

## Troubleshooting

| Problem | Cause | Fix |
| --- | --- | --- |
| 500 errors after deploy | D1 or R2 bindings missing in Pages settings | Add DB and MEDIA bindings under Pages > Settings > Functions |
| Admin shows setup wizard again | D1 is empty or wrong database is bound | Verify database ID matches what you created in D1 |
| Build fails | Node.js version mismatch | Set NODE_VERSION=20 in Pages environment variables |

That's it. Your EmDash site is live on Cloudflare's edge network, serving pages from 300+ data centers, with automatic SSL and zero server maintenance.
