Skip to main content
EmDash is a CMS built on Astro. It runs on Sevalla as an application built from a Dockerfile, keeps content, users and sessions in a Sevalla PostgreSQL database, stores uploaded media in object storage, and serves pages from Cloudflare with edge caching that the application purges through the Sevalla API when an editor publishes. This guide deploys Sevalla’s EmDash template, which has all of this set up.

Before you begin

  • A Sevalla company with a payment method, and a GitHub, GitLab or Bitbucket account for the repository. See Git providers.
  • Node.js 22.16 or later on your computer, to generate the encryption key.

Use the example repository

The EmDash template for Sevalla is EmDash’s blog template with the hosting layer adapted to Sevalla: Create an empty repository on your Git provider, such as acme/storefront, then copy the template into it and generate the key that encrypts EmDash’s plugin secrets:
Keep the generated key in a password manager: you add it as EMDASH_ENCRYPTION_KEY in the deployment steps, and EmDash can’t read its stored secrets without it.

Prepare the project for Sevalla

The template needs no code changes. Its parts depend on four Sevalla services, which you create in Deploy to Sevalla in the same location, because an internal connection only works within one location:
  • A PostgreSQL database. The database user that Sevalla creates owns the database, which EmDash needs because it creates and alters its own tables.
  • The application, built with the Dockerfile build strategy.
  • An object storage bucket with public access and a CORS rule, so the admin panel can upload from the browser.
  • An API key that can update the application, used to purge the edge cache.

Deploy to Sevalla

1

Create the database

In the sidebar, click Databases, then click Create database. Under Database, choose PostgreSQL and version 16 or newer. Enter orders-db as the Display name, choose a Data center and a Size, then click Create database. See Create a database for every field.
2

Create the application

In the sidebar, click Applications, then click Create > Application. Under Source, click Private repository, then choose the Git provider, the Repository and the Branch. Under Location, choose the same Data center as the database. Keep the Size at H1 or choose a larger one, then click Create application. The application opens on its Overview tab with the status Not deployed yet: creating an application doesn’t deploy it.
3

Set the build strategy

Click the Settings tab, then click Update build strategy. Choose Dockerfile, keep the Dockerfile path at Dockerfile and the Docker context at ., and click Update build strategy. While you’re on the Settings tab, copy the application’s ID: it’s the value of SEVALLA_APP_ID.
4

Connect the database

Click the Networking tab. Under Connected services, click Add internal connection. Choose orders-db as the Service, turn on Add environment variables to the application, and click Add internal connection. The template reads the DB_URL variable that Sevalla adds. See Connect an application.
5

Create the bucket

In the sidebar, click Object storage, then click Create object storage. Enter a Name, choose the Location closest to the application, then click Create object storage. See Create a bucket.
6

Open the bucket to the admin panel

On the bucket’s Settings tab, under Public access, click Enable public access and confirm. Then, under CORS policy, click Create rule. Select PUT, GET and HEAD under Allowed methods, enter the application’s address, such as https://storefront-x7k2m.sevalla.app, under Allowed origins and content-type under Allowed headers, then click Create CORS rule. See Bucket settings.
7

Create an API key

In the sidebar, under Tools, click API keys, then click Create API key. Choose Custom capabilities and allow only updating this application (the APP:UPDATE permission), then create the key and copy it. See API keys.
8

Add the environment variables

On the application’s Environment variables tab, click Add environment variables. Add these variables and click Add environment variables:The template reads them when the server starts, so they don’t need to be available during the build.
9

Turn on the CDN and edge caching

On the application’s Networking tab, under CDN & Edge caching, click Update settings. Turn on CDN and Edge caching, then click Done. See CDN and edge caching.
10

Deploy

Click the Overview tab, then click Deploy now under Recent deployments. Check the Branch and click Deploy.
11

Turn on auto-deploy

To deploy every push to the branch automatically, click the Settings tab, then click Update auto-deploy in the Auto-deploy section. Turn on Auto-deploy and click Update auto-deploy. See Deployments.
See Connect with the S3 API for where each bucket value comes from.

Verify the deployment

  1. On the Deployments tab, wait for the deployment’s status to change to Deployed. The application’s status next to its name changes to Running.
  2. Open the application’s address followed by /_emdash/admin, for example https://storefront-x7k2m.sevalla.app/_emdash/admin. The setup wizard creates the site, can load the demo content, and registers your admin passkey. EmDash applies its database migrations on the first request.
  3. Publish a post, then open the site’s home page. The post shows without waiting for the cached page to expire, because publishing purges the edge cache.
If the deployment fails, open it on the Deployments tab and read its build log, then see Troubleshoot builds and deployments. If the server stops with DATABASE_URL is not set in the runtime logs, the internal connection didn’t add its variables: add the connection again with Add environment variables to the application turned on. If media uploads fail in the admin panel, check the bucket’s CORS rule.

Deploy changes

With Auto-deploy on, every push to the branch starts a deployment; otherwise, click Deploy now for each change. Each deployment builds the image, then replaces the web process’s instances. See Deployments.

Add health checks

The template answers /healthz when the server runs and /healthz?db=1 when it can also reach PostgreSQL. Use them as the web process’s probes, so that Sevalla restarts an instance that stops responding and sends traffic only to instances that reach the database:
1

Open the web process

On the Processes tab, open the web process’s menu and click Update process.
2

Turn on the probes

Under Health checks, turn on Liveness probe with the Path /healthz, and Readiness probe with the Path /healthz?db=1. Click Update process. The probes apply at the next deployment.
See Processes for the other probe settings.

Run migrations before each deployment

Without a job, the first request after a deployment applies EmDash’s pending migrations. To apply them once, before new instances get traffic, add a job:
1

Create the job

On the Processes tab, click Create process and choose Job.
2

Set its command

Enter Migrate as the Name and npm run migrate as the Custom start command. Keep the Start policy at Before deployment, then click Create process.
The template’s migrate script runs emdash migrate without prompts. If the job fails, the deployment fails and nothing changes for visitors.

Add a custom domain

Custom domains need a web process larger than H1, so scale the web process first. After you add the domain on the Domains tab (see Custom domains):
  • Change EMDASH_SITE_URL to the new address and deploy again.
  • Add the new address to the bucket’s CORS rule. Rules can’t be updated: delete the rule and create a new one with both origins.
  • Register a passkey on the new domain before you remove the old one. Passkeys registered on the old address don’t work on the new one.

Scale the application

The template keeps no state in the instance, so you can run more than one instance or turn on autoscaling. Keep at least one instance running: EmDash’s scheduler for scheduled posts runs inside the web process. See Scaling.