What you will have
- One git repo with
supatype.config.ts(shared by local dev and the VM). - A VM running
supatype self-host compose(Kong gateway on port18473). - A GitHub Action that SSHs into the VM, pulls, migrates, rebuilds static files, and restarts Compose.
This guide is for self-host only. Supatype Cloud (managed Kubernetes) is a separate product and not covered here.
1 · Create the project locally
On your machine (Node.js 22+):
npm install -g @supatype/cli mkdir my-blog && cd my-blog supatype init npm install supatype keys
Copy the printed ANON_KEY and SERVICE_ROLE_KEY into .env for local dev.
Static frontend (one-time)
supatype app add --static ./public # Add a build step to package.json, e.g. Vite → public/ npm install -D vite # "build": "vite build" (outputDir: public)
Commit supatype.config.ts — do not run app add again on the server.
Develop locally
supatype dev supatype push
Optional: supatype.local.config.ts on your laptop only (proxy to Vite) while production stays static in the main config. See Deploy changes.
git init git add . git commit -m "Initial Supatype project" git branch -M main git remote add origin [email protected]:YOUR_USER/my-blog.git git push -u origin main
2 · Prepare the VM (one-time)
Ubuntu 22.04+ (or similar) with SSH access. Install Docker, Compose v2, Node 22, and the CLI:
# Docker — see https://docs.docker.com/engine/install/ubuntu/ sudo apt update && sudo apt install -y git nodejs npm sudo npm install -g @supatype/cli sudo mkdir -p /opt/my-blog sudo chown "$USER":"$USER" /opt/my-blog cd /opt/my-blog git clone [email protected]:YOUR_USER/my-blog.git .
Production secrets on the VM
Create /opt/my-blog/.env on the server (never commit this file). Use strong values and your public URL:
DATABASE_URL=postgresql://supatype_admin:CHANGE_ME@localhost:5432/my-blog POSTGRES_USER=supatype_admin POSTGRES_PASSWORD=CHANGE_ME POSTGRES_DB=my-blog JWT_SECRET=CHANGE_ME-long-random-string ANON_KEY=eyJ... # from supatype keys on first setup SERVICE_ROLE_KEY=eyJ... API_EXTERNAL_URL=https://api.example.com SITE_URL=https://example.com
First boot on the VM
cd /opt/my-blog npm install supatype self-host compose render supatype self-host compose up -d supatype push npm run build supatype self-host compose render supatype self-host compose up -d
Open http://YOUR_VM_IP:18473 (put Nginx/Caddy in front for HTTPS). Studio: /studio/.
3 · GitHub repository secrets
In your repo: Settings → Secrets and variables → Actions. Add:
VM_HOST— public IP or hostname of the VMVM_USER— SSH user (e.g.ubuntu)VM_SSH_KEY— private key (PEM) that can log in asVM_USERDEPLOY_PATH—/opt/my-blog(project directory on the VM)
The workflow does not upload .env; production secrets stay only on the VM.
4 · GitHub Actions workflow
Add .github/workflows/deploy-self-host.yml to your repo:
name: Deploy self-host
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy over SSH
uses: appleboy/[email protected]
with:
host: ${{ secrets.VM_HOST }}
username: ${{ secrets.VM_USER }}
key: ${{ secrets.VM_SSH_KEY }}
script_stop: true
script: |
set -euo pipefail
cd "${{ secrets.DEPLOY_PATH }}"
git fetch origin main
git reset --hard origin/main
npm ci
supatype push
npm run build
supatype self-host compose render
supatype self-host compose up -d
On every push to main, the VM pulls latest code, applies schema migrations, rebuilds public/, and refreshes Compose.
supatype push uses DATABASE_URL from the VM’s .env (Postgres exposed on localhost:5432 by Compose).
Install @supatype/cli globally on the VM, or change the script to npx supatype@latest.
5 · Verify the pipeline
# Local: change schema or frontend, then git add . git commit -m "Add posts table and update landing page" git push origin main
Watch the Action in GitHub → Actions. On success, hit your gateway URL and confirm schema + static files updated.
Troubleshooting
- SSH fails — check security group / firewall allows port 22; verify
VM_SSH_KEYmatches the VM’sauthorized_keys. supatype pushfails — ensure Compose is up andDATABASE_URLpoints atlocalhost:5432.- Site unchanged — confirm
npm run buildwrites into the directory set byapp.static_dir(e.g../public), then re-runcompose render+up -d.