- contact@verticalserve.com
- Continuous compliance, proven every hour
The same images VerticalServe runs, on one machine or your cloud, with your database, storage and model provider.
Self-hosted InsightAssure is three containers: Postgres 16, the API and the console (nginx, which also proxies /api). You need Docker with Compose.
cd deploy/selfhost
./install.sh # asks for the organization name and administrator email,
# writes .env with generated passwords, then: docker compose up -dThe console is on http://<host>:8080. The first start runs the migrations (about a minute). .env is written with mode 600 and holds the generated Postgres and application passwords and the first administrator’s password; change that password after signing in. For anything beyond a trial, put a TLS-terminating proxy or your load balancer in front and set the two URLs below to the https address.
| Service | Image | Notes |
|---|---|---|
postgres | postgres:16-alpine | Volume pg. Health-checked before the API starts |
api | vserveinc/insightassure-api | Port 8136 inside the network. Volume data at /data. Runs as an unprivileged user |
ui | vserveinc/insightassure-ui | Publishes ${ASSURE_HTTP_PORT:-8080}; proxies /api to ASSURE_API_UPSTREAM (api:8136) |
On first start the API uses ASSURE_DB_SUPERUSER_URL to create the application role assure_app as an ordinary role, NOSUPERUSER NOBYPASSRLS, with a 30-second statement timeout. That matters: Postgres row-level security does not bind a superuser or a role with BYPASSRLS, so the app must connect as an ordinary role for the per-organization policy to apply. The migration step warns if it is not.
| Variable | Meaning |
|---|---|
ASSURE_EDITION | selfhosted here (hides the multi-workspace portfolio); saas on VerticalServe’s hosting |
INSIGHT_DATABASE_URL | The app’s Postgres URL, without a password: postgresql://assure_app@postgres:5432/assure |
INSIGHT_DATABASE_PASSWORD / INSIGHT_DATABASE_SECRET | The password for a local database, or a secret ref (aws-sm://, azure-kv://, gcp-sm://, file:///run/secrets/pg, env://VAR) resolved at start |
INSIGHT_DATABASE_SCHEMA | assure: the product’s tables, identity, connections and notifications share this schema |
INSIGHT_DATABASE_SSLMODE | disable inside the compose network; require for a managed database |
ASSURE_DB_SUPERUSER_URL | Used once to create assure_app; drop it when you manage the role yourself |
ASSURE_ORG, ASSURE_ORG_NAME | The organization slug and display name |
ASSURE_ADMIN_EMAIL, ASSURE_ADMIN_PASSWORD | The first administrator, created only if absent; a restart never resets a password |
ASSURE_PUBLIC_URL, ASSURE_CONSOLE_URL | Where the API and console are reached: used in agent install commands, Trust Center links and notifications |
ASSURE_EVIDENCE_STORE | Evidence files, policy PDFs and audit packages: file:///data/evidence (default), s3://bucket/prefix, az://account/container, gs://bucket, oci://namespace/bucket |
ASSURE_AI | false (the self-host default) answers every AI feature by rule; true uses the model below |
INSIGHT_LLM_PROVIDER | bedrock, azure_openai, vertex, oci or openai (any OpenAI-compatible endpoint, with INSIGHT_LLM_BASE_URL) |
INSIGHT_CHAT_MODEL, INSIGHT_LLM_API_KEY_REF | The model id, and a secret ref to its key for key-based providers |
INSIGHT_SEALED_SECRETS | Where integration credentials are sealed: auto (a configured secret store, else local AES-GCM), store or local |
INSIGHT_SECRET_KEY_FILE / INSIGHT_SECRET_KEY_REF | The key that seals credentials locally; the image keeps it at /data/secret.key |
INSIGHT_JWT_KEY_FILE / INSIGHT_JWT_KEY_REF | The token-signing key; generated once at /data/jwt-key.json unless you provide one |
ASSURE_ALLOW_PRIVATE_INTEGRATIONS | true lets integrations call private addresses, for a Jira or GitLab inside your network. Off by default |
ASSURE_BACKGROUND, ASSURE_MONITOR_INTERVAL_MINUTES | The background loop (syncs, monitors, reminders) and how often monitors run (60) |
CORS_ORIGINS, LOG_FORMAT, ASSURE_METRICS_TOKEN | Console origins when it is served from another host; json logs; a bearer token for /api/metrics |
Integration credentials are never stored in clear and never returned by the API: they are sealed with insight_core.secrets.sealed, in your secret store when one is configured (one secret per connection) or as one AES-256-GCM token bound to the row. A field may instead hold ref:aws-sm://… to point at a secret you already keep.
Back up two things: the Postgres volume (pg_dump or your managed backups) and the data volume, which holds jwt-key.json, secret.key and, with the default store, the evidence files. Losing secret.key makes every sealed integration credential unreadable; reconnect the integrations if it happens.
docker compose exec postgres pg_dump -U postgres assure > assure-$(date +%F).sql
docker run --rm -v insightassure_data:/data -v "$PWD":/backup alpine tar czf /backup/assure-data-$(date +%F).tgz -C /data .Pull the new images and restart. Migrations run on every start under a Postgres advisory lock, so two API replicas starting together never race.
Upgradedocker compose pull && docker compose up -d