Skip to content
Gitupcms
Esc
↑↓navigate↵open⌘Jpreview
On this page

Server

Run Gitupcms on a Linux VM with systemd and Caddy. This is how app.gitupcms.com runs.

app.gitupcms.com runs on an AWS Lightsail instance (4 GB, Ubuntu) with Caddy in front for HTTPS. Any Linux VM works the same way.

Install Node, pnpm and Caddy

curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt-get install -y nodejs build-essential git rsync
sudo npm i -g pnpm@10
# Caddy: see caddyserver.com/docs/install

Create a service user and folders

sudo useradd --system --home /var/lib/gitupcms --shell /usr/sbin/nologin gitupcms
sudo mkdir -p /opt/gitupcms /var/lib/gitupcms/data /var/lib/gitupcms/previews
sudo chown -R gitupcms:gitupcms /opt/gitupcms /var/lib/gitupcms

Write the environment

BETTER_AUTH_URL=https://cms.example.com
BETTER_AUTH_SECRET=...
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
DB_PATH=/var/lib/gitupcms/data/gitupcms.db
HOST=127.0.0.1
PORT=4321
PREVIEW_RUNNER=1
PREVIEW_DIR=/var/lib/gitupcms/previews
PREVIEW_DOCKER=node:24-bookworm-slim

Make it readable only by root and the service: chown root:gitupcms, chmod 640.

PREVIEW_DOCKER runs each site’s install and astro dev in a container. Install Docker, let the service use it and keep containers off the cloud metadata address:

curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker gitupcms
sudo docker pull node:24-bookworm-slim
sudo iptables -I DOCKER-USER -d 169.254.169.254 -j DROP   # persist with iptables-persistent

Docker group membership is root-equivalent for the service user; leave PREVIEW_DOCKER unset if you don’t run previews.

Copy and build

rsync -az --exclude node_modules --exclude dist --exclude .env --exclude data ./cms/ server:/opt/gitupcms/
sudo -u gitupcms sh -c 'cd /opt/gitupcms && pnpm install --frozen-lockfile && pnpm build'

Run it with systemd

[Unit]
Description=Gitupcms
After=network.target

[Service]
User=gitupcms
Group=gitupcms
WorkingDirectory=/opt/gitupcms
EnvironmentFile=/etc/gitupcms.env
Environment=NODE_ENV=production
ExecStart=/usr/bin/node ./dist/server/entry.mjs
Restart=always
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/var/lib/gitupcms /opt/gitupcms /tmp

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now gitupcms

HTTPS with Caddy

cms.example.com {
  encode zstd gzip
  reverse_proxy 127.0.0.1:4321 {
    flush_interval -1
  }
}

flush_interval -1 keeps the AI Editor’s streamed answers flowing. Point a DNS A record at the server and reload Caddy; it gets the certificate itself.

Live preview (optional)

With PREVIEW_HOST=https://*.preview.cms.example.com, the AI Editor runs each page’s scripts on its own subdomain. Add a wildcard A record (*.preview.cms.example.com) and a wildcard certificate, which needs a DNS challenge: a Caddy build with your DNS provider (caddy-dns), e.g. Route 53:

*.preview.cms.example.com {
  tls {
    dns route53
  }
  reverse_proxy 127.0.0.1:4322 {
    flush_interval -1
  }
}

Give Caddy the DNS credentials through a systemd EnvironmentFile, and drop --environ from its ExecStart, or it writes them to the log.

Updating

Copy the new source, then build and restart:

sudo -u gitupcms sh -c 'cd /opt/gitupcms && pnpm install --frozen-lockfile && pnpm build'
sudo systemctl restart gitupcms

Logs and backups

  • Logs: journalctl -u gitupcms -f. Without SMTP_URL, email sign-in codes appear here.
  • Back up the SQLite file in DB_PATH (for example with sqlite3 gitupcms.db ".backup backup.db"). Content is in git; the database holds users, workspaces, settings, media metadata, comments and feedback.

Was this page helpful?