Traditional Server

Run Dune on any machine with Deno installed — a VPS, a dedicated server, or your own hardware.

Production setup

1. Install Deno

curl -fsSL https://deno.land/install.sh | sh

2. Get your site onto the server and add production overrides

Put your site's repository on the server (any method works; Git is the usual one):

git clone <your-site-repo-url> my-site
cd my-site

Dune merges config/site.yaml and config/system.yaml with any files in config/env/<name>/, where <name> is the value of the DUNE_ENV environment variable. Step 3 sets DUNE_ENV=production, so everything in config/env/production/ applies on this server and nowhere else. Only list what differs from your other environments.

config/env/production/site.yaml:

url: "https://example.com"

config/env/production/system.yaml:

cache:
  lifetime: 7200

The production defaults are already sensible: debug: false, and the filesystem cache is enabled with the file staleness check. You don't need to repeat them. See System Config for the other cache drivers and the hash check.

3. Build and serve

# Build the content index
DUNE_ENV=production dune build

# Start the server
DUNE_ENV=production dune serve --port 8000

4. Reverse proxy

Put Dune behind nginx or Caddy for HTTPS and static asset caching:

server {
    server_name example.com;
    listen 443 ssl;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # Serve static files directly
    location /static/ {
        root /path/to/my-site;
        expires 30d;
    }
}

Process management

Use systemd or a process manager to keep Dune running:

# /etc/systemd/system/dune.service
[Unit]
Description=Dune CMS
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/path/to/my-site
Environment=DUNE_ENV=production
ExecStart=deno run -A main.ts serve
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

This is the main.ts entrypoint pattern — what dune new scaffolds and what dune migrate:entrypoint moves existing sites onto. Pass --frozen (or set DUNE_FROZEN=1) to make Dune refuse startup if deno.lock is incomplete, preventing silent lockfile drift on the server; run dune lockfile:sync locally and commit the result before deploying either way. See Invocation Patterns for the full workflow, --frozen's current status, and the legacy JSR-URL/installed-binary patterns for sites that haven't migrated yet.

Updating content

On a traditional server, content lives on the filesystem. Update it however you like:

  • Git pull: git pull origin main && dune cache:rebuild
  • rsync: Sync from a local machine
  • SFTP: Upload changed files
  • Admin panel: Available at /admin — manage content, users, and settings through the browser

After updating content, rebuild the index:

dune cache:rebuild

Or run in dev mode which watches for changes automatically.