Run Locally & Self-Host
The code is yours. Run it on your laptop or on your own server.
A Totalum project is a standard Next.js app. Run it locally to develop with your own tools, or host it on your own server instead of Totalum's hosting. Its database and built-in features (e-mail, AI, files…) keep working through the project key in its .env, over HTTPS — there is no database to install. To take the database and files with you as well, see Move to Another Host.
You need Node.js 22 and npm, the project id, and your API key (tlm_sk_…, server-side only).
#Run it locally
export API_KEY="tlm_sk_your_key" PROJECT_ID="my-app" BASE="https://builder-api.totalum.app/api/v1/vcaas"
# 1) the source code (a signed link to a .tar.gz, valid a few minutes)
URL=$(curl -sf -H "api-key: $API_KEY" "$BASE/projects/$PROJECT_ID/source-code" | jq -r '.data.downloadUrl')
mkdir my-app && curl -sf "$URL" | tar -xz -C my-app && cd my-app
# 2) the development .env (the preview's database and keys)
curl -sf -H "api-key: $API_KEY" "$BASE/projects/$PROJECT_ID/env?env=dev" -o .env
# 3) run it
npm ci
npm run dev # http://localhost:3100npm run dev applies the project's migrations to the development database and serves the app on it — the same data as the preview. Built-in features are billed to the project as usual. Useful commands:
| Command | What it does |
|---|---|
npm run db:generate | After you change src/db/schema.ts: writes the next migration to drizzle/ |
npm run db:migrate | Applies the migrations to the database TOTALUM_ENV selects (dev or live). Without TOTALUM_ENV it refuses on a project with a development database, so it never changes the live one by default |
npm run db:sql -- "SELECT …" | Runs one SQL statement on the database |
npm run logs -- --level error | Reads the app's logs |
npm run check | The build and type check Totalum runs before deploying |
Changes you make locally reach Totalum through GitHub (push to develop) or the file API.
#Host it on your own server
Any Linux server with Node 22 works (Ubuntu 22.04+ / Debian 12, 1 GB RAM). The app talks to its live database and features over HTTPS with the project key from the production .env.
#1. Download the code and the production .env
export API_KEY="tlm_sk_your_key" PROJECT_ID="my-app" BASE="https://builder-api.totalum.app/api/v1/vcaas"
APP_DIR="$HOME/my-app"
URL=$(curl -sf -H "api-key: $API_KEY" "$BASE/projects/$PROJECT_ID/source-code" | jq -r '.data.downloadUrl')
mkdir -p "$APP_DIR" && curl -sf "$URL" | tar -xz -C "$APP_DIR"
curl -sf -H "api-key: $API_KEY" "$BASE/projects/$PROJECT_ID/env?env=live" -o "$APP_DIR/.env"
chmod 600 "$APP_DIR/.env"Then add your domain to .env before building:
cat >> "$APP_DIR/.env" <<'EOF'
TOTALUM_PUBLIC_HOSTS=app.yourdomain.com
TOTALUM_TRUST_PROXY=1
EOF
sed -i 's#^NEXT_PUBLIC_APP_URL=.*#NEXT_PUBLIC_APP_URL=https://app.yourdomain.com#' "$APP_DIR/.env"TOTALUM_PUBLIC_HOSTS gives canonical links, the sitemap and sign-in your domain; TOTALUM_TRUST_PROXY=1 lets the app read its public https:// address from the proxy in step 3.
#2. Build and run
cd "$APP_DIR"
npm ci
npm run build
PORT=3000 npm start # binds 0.0.0.0:3000#3. Keep it running (systemd) and serve HTTPS (Caddy)
/etc/systemd/system/my-app.service:
[Unit]
Description=Totalum project (Next.js)
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/my-app
Environment=PORT=3000
ExecStart=/usr/bin/npm start
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload && sudo systemctl enable --now my-app/etc/caddy/Caddyfile (Caddy gets and renews the certificate; point your domain's A record at the server first):
app.yourdomain.com {
reverse_proxy 127.0.0.1:3000 {
# the visitor's IP, which the login rate limits count by
header_up CF-Connecting-IP {remote_host}
}
}sudo systemctl reload caddy#4. Redeploy after every change
#!/usr/bin/env bash
# deploy.sh — pull the newest code and .env, rebuild, restart
set -euo pipefail
: "${API_KEY:?set API_KEY}"
PROJECT_ID="my-app"; APP_DIR="/home/ubuntu/my-app"; PUBLIC_URL="https://app.yourdomain.com"
BASE="https://builder-api.totalum.app/api/v1/vcaas"
URL=$(curl -sf -H "api-key: $API_KEY" "$BASE/projects/$PROJECT_ID/source-code" | jq -r '.data.downloadUrl')
curl -sf "$URL" | tar -xz -C "$APP_DIR"
curl -sf -H "api-key: $API_KEY" "$BASE/projects/$PROJECT_ID/env?env=live" -o "$APP_DIR/.env"
sed -i "s#^NEXT_PUBLIC_APP_URL=.*#NEXT_PUBLIC_APP_URL=$PUBLIC_URL#" "$APP_DIR/.env"
printf 'TOTALUM_PUBLIC_HOSTS=%s\nTOTALUM_TRUST_PROXY=1\n' "${PUBLIC_URL#https://}" >> "$APP_DIR/.env"
cd "$APP_DIR" && npm ci && npm run build && sudo systemctl restart my-app
echo "Deployed: $PUBLIC_URL"Database changes reach the live database when you publish in Totalum (or run npm run db:migrate with the live .env); deploy the code after them.
#Troubleshooting
| Symptom | Fix |
|---|---|
| Sign-in fails or loops | Serve over HTTPS, set TOTALUM_PUBLIC_HOSTS to the domain and TOTALUM_TRUST_PROXY=1 behind the proxy, then rebuild |
SDK_NOT_CONFIGURED errors | The .env is missing or not next to the app: download it again |
403 downloading the source | The signed link expired: ask for a new one |
| A page fails after a schema change | Publish in Totalum (or run the migrations) before deploying the new code |
It holds the project key and the sign-in secret: never commit it, never expose it to the browser, keep it chmod 600. Rotate a leaked project key with Rotate Project Key.
