DEV Community

Matthias Andrasch
Matthias Andrasch

Posted on

Quick test of EmDash CMS deployment on Hetzner VPS (Node.js)

EmDash is a new CMS made by Cloudflare, which describes itself as the „spiritual successor to WordPress (that solves plugin security)“ and as „an open source Astro CMS built for humans and agents“.

It can be deployed via Cloudflare (obviously), but selfhosting is also possible.

Here is a step-by-step guide to deploy EmDash CMS on a fresh Hetzner VPS using Node.js, SQLite, Caddy webserver, PM2 (node process manager), and sslip.io for automatic SSL certificates without needing a custom domain name (for now).

Beware: This is just for demo purposes, security needs to be checked. Use a hetzner firewall and only open port 22 and 443 for example.

For this tutorial, I quickly purchased a Hetzner CX23 VPS, Ubuntu 26.

Connect to the VPS

ssh root@<IP-of-hetzner-VPS>
Enter fullscreen mode Exit fullscreen mode

Install Node.js & pnpm

# Download and install nvm (Node Version Manager):
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.8/install.sh | bash

# Load nvm into the current shell session in lieu of restarting the terminal
\. "$HOME/.nvm/nvm.sh"

# Download and install Node.js version 24:
nvm install 24

# Verify the Node.js version prints "v24.22.0":
node -v

# Enable and install pnpm via Node's corepack:
corepack enable pnpm

# Verify the installed pnpm version:
pnpm -v
Enter fullscreen mode Exit fullscreen mode

Install Caddy webserver


# 1. Update package lists and install prerequisite packages for Caddy (curl and gnupg)
apt update && apt install -y curl gnupg

# 2. Download and add the official Caddy GPG signing key to secure apt repositories
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
# Add the Caddy official repository source list configuration
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list

# 3. Update APT package lists again to include the new Caddy repository and install Caddy web server
apt update
apt install -y caddy
Enter fullscreen mode Exit fullscreen mode

Check that the Caddy background service is active and running correctly: systemctl status caddy

Git pull the demo project

For this demo, I simply installed the marketing starter via pnpm create emdash@latest ...

screenshot of emdash install wizard

screenshot of emdash install wizard

... and pushed it to a public GitHub repository on my account (
https://github.com/mandrasch/emdash-demo-marketing-starter).

This is what we'll deploy:

# Create a central web root directory for hosting websites
mkdir /var/www

# Change current directory into the web root
cd /var/www

# Clone the EmDash marketing starter demo project repo
git clone https://github.com/mandrasch/emdash-demo-marketing-starter.git

# Move inside the cloned project directory
cd emdash-demo-marketing-starter

# Install project dependencies using pnpm and compile the production application bundle to serve the site later
pnpm i && pnpm run build
Enter fullscreen mode Exit fullscreen mode

Setup the demo project

# Generate a secure random cryptographic key for encrypting plugin secrets in EmDash
EMDASH_KEY=$(pnpm dlx emdash secrets generate)

# Fetch the server's public IPv4 address from an external API and store it in a variable
SERVER_IP=$(curl -s https://api.ipify.org)

# Create and write the runtime environment configuration file (.env) with server settings
cat << EOF > /var/www/emdash-demo-marketing-starter/.env
PORT=4321
HOST=0.0.0.0
EMDASH_SITE_URL=https://${SERVER_IP}.sslip.io
DATABASE_PATH=/var/www/emdash-demo-marketing-starter/data/emdash.db
EMDASH_ENCRYPTION_KEY=${EMDASH_KEY}
EOF

# Create local directories required for storing SQLite data files and uploaded media assets
mkdir -p /var/www/emdash-demo-marketing-starter/data/uploads
Enter fullscreen mode Exit fullscreen mode

Install pm2

PM2 is a daemon process manager for Node.js applications.

# Configure pnpm global bin paths into the shell configuration and reload it
pnpm setup && source ~/.bashrc

# Install PM2 globally as a Node.js process manager to keep apps running in the background
pnpm add -g pm2
Enter fullscreen mode Exit fullscreen mode

Run our site (via pm2)

# Change directory back to the project folder before starting the process
cd /var/www/emdash-demo-marketing-starter

# Start the EmDash production server instance via PM2 using the Node.js binary and environment file
pm2 start /root/.nvm/versions/node/v24.22.0/bin/node --name "emdash" -- --env-file=.env ./dist/server/entry.mjs
Enter fullscreen mode Exit fullscreen mode

The command pm2 list now shows the running emdash site:

output of pm2 list command

See the pm2 cheatsheet for all commands.

Configure pm2 to launch on server restarts

# Generate and configure a systemd startup script so PM2 automatically restarts apps on server reboots
pm2 startup systemd -u root --hp /root
# Save the currently running PM2 process list so it persists across reboots
pm2 save
Enter fullscreen mode Exit fullscreen mode

Create the Caddy configuration

# Fetch the public IP address again to ensure it's available for the Caddyfile configuration block
SERVER_IP=$(curl -s https://api.ipify.org)

# Create a Caddyfile configuration to reverse-proxy traffic from your sslip.io domain to port 4321

cat << EOF | sudo tee /etc/caddy/Caddyfile
${SERVER_IP}.sslip.io {
    reverse_proxy localhost:4321
}
EOF

# Reload Caddy to apply the new reverse proxy configuration and automatically generate an SSL certificate
sudo systemctl reload caddy
Enter fullscreen mode Exit fullscreen mode

Access your site and finish initial installation

Open your browser to test the public site, the images won't be there yet: https://<YOUR-SERVER-IP>.sslip.io

Create the admin account:

https://<YOUR-SERVER-IP>.sslip.io/_emdash/admin

Install step 1, site title

Install step 2, user and email

In step 3, you can generate a passkey for your admin account.

(There is no current option to use a regular password out of the box, see my recent bluesky post for more information about it.)

After the finished setup, the images will be also visible on https://<YOUR-SERVER-IP>.sslip.io:

screenshot of demo website

And you can edit your content in the backend:

Screenshot of pages section

Screenshot of homepage editing

I could not find the setting to lock down the Admin > Content Types section for now, but I'll re-check out the docs.

Where to go from here

This was fairly simple, but to really evaluate it professionally we would need to check the following things:

What would you test first? And what is your first impression of EmDash?

More guides

Top comments (0)