Docs
BlogHomeStart building

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

bash
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:3100

npm 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:

CommandWhat it does
npm run db:generateAfter you change src/db/schema.ts: writes the next migration to drizzle/
npm run db:migrateApplies 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 errorReads the app's logs
npm run checkThe 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

bash
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:

bash
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

bash
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:

ini
[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.target
bash
sudo 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):

caddyfile
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}
    }
}
bash
sudo systemctl reload caddy

#4. Redeploy after every change

bash
#!/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

SymptomFix
Sign-in fails or loopsServe over HTTPS, set TOTALUM_PUBLIC_HOSTS to the domain and TOTALUM_TRUST_PROXY=1 behind the proxy, then rebuild
SDK_NOT_CONFIGURED errorsThe .env is missing or not next to the app: download it again
403 downloading the sourceThe signed link expired: ask for a new one
A page fails after a schema changePublish in Totalum (or run the migrations) before deploying the new code
Keep the .env secret

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.