Operate
Self-hosting
Run Globatics on your own server with Node.js or Docker, configure the database, and back it up.
Globatics is one Node.js app and one PostgreSQL database. Without a database URL it runs an embedded PostgreSQL (PGlite) in a local folder, so there is nothing else to install. No queue or cache, and every feature is included. A small virtual server is enough to start, and it also runs on Vercel with Supabase.
Requirements
- Node.js 22.13 or newer, 24 LTS recommended.
- PostgreSQL 17 (any host, Supabase included) set in
DATABASE_URL, or a persistent disk for the embedded database. - Room for the data. As a rough guide, 90 days of history at about 4,000 sessions a day (356,000 sessions and 1.1 million events) take about 800 MB.
- HTTPS in front of it for production (any reverse proxy, or a platform that terminates TLS).
Run with Node.js
# in your copy of the Globatics repository
npm ci
NEXT_PUBLIC_APP_URL=https://analytics.example.com npm run build
DATABASE_URL=postgres://user:password@host:5432/postgres \
NEXT_PUBLIC_APP_URL=https://analytics.example.com \
npm start # listens on port 3000 (PORT=8080 to change it)On first start the database is created and migrated. Open the server in a browser, create your account and add your first site. Run it under a process manager (systemd, pm2…) so it restarts on reboot.
Run with Docker
The repository ships a production Dockerfile (multi-stage, Node.js 24): it builds the tracker and the app, then runs next start as an unprivileged user, migrates the database on start, and a health check calls /api/health. With DATABASE_URL you can run as many containers as you like. Without it the embedded database lives on the /app/data volume: run one container per volume.
docker build -t globatics --build-arg NEXT_PUBLIC_APP_URL=https://analytics.example.com .
docker run -d --name globatics --init --restart unless-stopped \
-p 3000:3000 \
-e DATABASE_URL=postgres://user:password@host:5432/postgres \
-e GLOBATICS_TRUST_PROXY=1 \
globaticsThe server secret
Without GLOBATICS_SECRET, a random secret is generated on first start and stored in the database, so webhook signatures survive restarts and backups. If you set it yourself (several instances, rotation), generate it once and keep it, for example in a .env file: a new value changes every site’s webhook signing secret.
Environment variables
| Variable | Default | Purpose |
|---|---|---|
GLOBATICS_SECRET | generated once, stored in the database | Signs alert webhooks. Set it (32+ random characters) to share one value across instances or to rotate it; keep it stable. |
NEXT_PUBLIC_APP_URL | http://localhost:3000 | Public URL of the server (links, social images, docs snippets). Read at build time. |
GLOBATICS_PUBLIC_URL | — | Public URL used in webhook deep links. |
DATABASE_URL | — | PostgreSQL 17 connection string (Supabase: the transaction pooler). Unset: the embedded database below. Never commit it. |
DATABASE_LISTEN_URL | — | Session-mode connection used for LISTEN, so live views update across instances. Unset: they poll every second. |
GLOBATICS_PGLITE_DIR | ./data/pglite | Folder of the embedded database (PGlite), used when DATABASE_URL is unset. One server per folder. |
GLOBATICS_AUTO_MIGRATE | 1 | Set to 0 to skip migrations at startup and run npm run db:migrate yourself. |
GLOBATICS_TRUST_PROXY | 0 | Trust X-Forwarded-For and CDN geo headers. Set to 1 behind a reverse proxy or CDN. |
GLOBATICS_RATE_LIMIT | 30,60 | Ingest rate limit per site and client: hits per second, burst. |
GLOBATICS_ALERTS | 1 | Alerts engine on or off. |
GOOGLE_CLIENT_ID | — | Optional. OAuth client ID that turns on “Continue with Google” (see Sign in with Google below). |
GOOGLE_CLIENT_SECRET | — | Optional. Secret of that OAuth client. Both must be set; never commit it. |
GLOBATICS_INSECURE_COOKIES | — | Send the session cookie without Secure (plain-HTTP setups only). |
GLOBATICS_DEV_GEO | — | Location used for visits from localhost or private IPs: City,CC,Region,lat,lng. |
GLOBATICS_DEMO | on in npm run dev | Development only: seeds a local demo account and site and runs a traffic simulator. Always off in production. |
ANTHROPIC_API_KEY | — | Optional. Lets Claude answer Ask questions and polish digests. Everything works without it. |
Behind a reverse proxy
Set GLOBATICS_TRUST_PROXY=1 and make sure the proxy forwards the visitor’s address, or every visitor gets the proxy’s location. The live view uses Server-Sent Events: don’t buffer them.
analytics.example.com {
reverse_proxy localhost:3000
}Sign in with Google
“Continue with Google” appears on the sign-in and sign-up pages once GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are set. Without them the button is hidden and the Google routes answer 404. It uses OpenID Connect (authorization code with PKCE) and only asks for the name and verified email address.
- In the Google Cloud console, open APIs & Services → Credentials, create an OAuth client ID of type Web application, and configure the consent screen (app name, support email, the
openid,emailandprofilescopes). - Authorized JavaScript origin: your public origin, for example
https://analytics.example.com. - Authorized redirect URI:
https://analytics.example.com/api/auth/google/callback, exactly (scheme, host, no trailing slash). - Set both variables and restart. The redirect URI is built from the public origin (
GLOBATICS_PUBLIC_URL, thenNEXT_PUBLIC_APP_URL, then Vercel’s production domain), never from a Vercel deployment URL, so it matches what you registered.
GOOGLE_CLIENT_ID=1234567890-abc.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-…A Google account whose verified email matches an existing account is connected to it on first use; otherwise a new account is created, without a password. Users manage the connection, or set a password, in Account → Connected accounts.
Backups
Everything lives in the globatics schema of your database. Take a consistent copy without stopping the server (or use your host’s backups, such as Supabase’s):
pg_dump "$DATABASE_URL" --schema=globatics --format=custom \
--file="backups/globatics-$(date +%F).dump"To restore, stop the server and run pg_restore --clean --dbname="$DATABASE_URL" on the dump. With the embedded database, stop the server and copy its folder (GLOBATICS_PGLITE_DIR).
Upgrades
git pull
npm ci
npm run build
# restart the process: database migrations run automatically on bootBack up before upgrading. Questions about what is stored? See Data fields and privacy.