Dokploy (Docker)
The site is server-rendered with the Astro Node adapter and the EmDash CMS, so it
deploys as a long-running Node server rather than static files. The repo ships a
multi-stage Dockerfile that builds a standalone server (dist/server/entry.mjs).
Deploy on Dokploy
Section titled “Deploy on Dokploy”- Push the repo to GitHub.
- In Dokploy, create an Application from the repository using the Dockerfile build.
- Mount a persistent volume at
/app/data. This holds the SQLite database (emdash.db) and any local uploads, so content survives redeploys. - Set environment variables (see Environment Variables):
SITE_URL, theS3_*Cloudflare R2 credentials, and any optional analytics keys. - Expose port 4321 and point your domain at the service.
- Deploy. On a fresh volume, the entrypoint seeds content automatically (below).
Cloudflare R2 media
Section titled “Cloudflare R2 media”In production, media is stored in Cloudflare R2 through its S3-compatible API. Create
an R2 bucket and an S3 API token, then set S3_ENDPOINT, S3_BUCKET,
S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY. If S3_BUCKET is unset, EmDash falls
back to local filesystem storage under ./data/uploads.
Uploads PUT directly to R2 from the browser via presigned URLs, so the bucket needs a
CORS policy allowing your site origin (GET, PUT, HEAD). The bucket can stay
private — EmDash serves media through its own proxy route
(/_emdash/api/media/file/<key>) that streams objects from R2, so URLs always look
same-origin and S3_PUBLIC_URL is not needed. See
Environment Variables → Media storage for the
exact CORS policy and details.
Database, migrations & seeding
Section titled “Database, migrations & seeding”The SQLite database is runtime state and is not committed to git - it lives on the
/app/data volume. On deploy:
- The server runs schema migrations automatically on the first request, creating the database and tables on the volume.
- The container
docker-entrypoint.shrunsemdash seedfromseed/seed.jsononly when no database exists yet (a fresh volume), so a first deploy comes up populated and redeploys never overwrite your data.
To seed or re-seed manually (e.g. outside Docker):
pnpm exec emdash seed --database data/emdash.db --uploads-dir data/uploadsAfterwards, content is edited through the admin panel at /_emdash/admin (create the
owner account on first visit) and requires no redeploy.
Local Docker test
Section titled “Local Docker test”docker build -t astro-emdash .docker run --rm -p 4321:4321 -v "$PWD/data:/app/data" astro-emdash