Self-hosting operations
Self-hosted Radioso has two durable data surfaces:
- PostgreSQL, including accounts, workspaces, settings, conversations, audit events, documents, chunks, vectors, and worker queues
- uploaded source-file storage, either the local document-storage volume or the configured object-storage bucket
Back up both surfaces together. A database backup without source files can leave document records whose original uploads cannot be reprocessed. Source files without the database are not enough to restore accounts, settings, embeddings, or chat history.
Minimum operating baseline
For a small private deployment, the minimum baseline is:
- run the backend, frontend, document worker, crawler worker, PostgreSQL, and document storage as separate services or containers
- put the frontend and backend behind HTTPS
- keep
.envand secret values outside source control - back up PostgreSQL and document storage every day
- test restore before relying on the backup
- pin the deployed image tag or Git commit
- apply upgrades manually after reading the migration list
Backup
For Docker Compose deployments, back up:
- the PostgreSQL database with
pg_dumpor a physical volume snapshot - the
radioso_document_storagevolume whenDOCUMENT_STORAGE_DRIVER=local - the deployed
.envor equivalent secret inventory, stored in a secure password manager or secrets system
For cloud deployments, back up:
- Cloud SQL or the managed PostgreSQL database
- the GCS bucket or object-storage bucket used by
DOCUMENT_STORAGE_BUCKET - Secret Manager values or the external secret inventory
Keep database and source-file backups from the same time window. That makes document reprocessing predictable after restore.
Restore
Stop writers
Stop the backend, document worker, and crawler worker before restoring. This prevents new writes while the database and file storage are out of sync.
Restore PostgreSQL
Restore the database first. Confirm the schema_migrations table exists and matches the deployed code version.
Restore source files
Restore the local document-storage volume or object-storage bucket. Keep object paths unchanged.
Start the backend
Start the backend and confirm /health returns success. The backend owns SQL migrations, so it should be the first runtime to touch the restored database.
Start workers
Start the document worker and crawler worker. Watch for pending or failed processing jobs before opening the deployment to users.
Upgrade
Use this order for self-hosted upgrades:
- Read the release notes or inspect
backend/src/db/migrations/for new SQL migrations. - Back up PostgreSQL and document storage.
- Build or pull the new backend, frontend, and worker images.
- Start the backend and let migrations run.
- Start the document worker and crawler worker.
- Start or refresh the frontend.
- Upload a small document and confirm it becomes searchable.
- Ask one grounded question and verify citations or retrieval trace data.
Do not auto-pull main into production without a backup. A failed application upgrade is usually reversible; a partially applied schema or mismatched storage restore is harder to recover.
The backend runs SQL migrations during startup before it opens the HTTP port. The document worker and crawler worker only check for pending migrations. Start the backend first, then start workers after the backend has completed migrations and /health responds.
Migration startup metadata checks have their own timeout controls:
DB_MIGRATION_LOCK_TIMEOUT_MSDB_MIGRATION_STATEMENT_TIMEOUT_MS
Keep these values shorter than your platform startup-probe window. That way a blocked migration metadata check appears as a clear application log instead of a silent port-listen timeout. Large migration SQL bodies, such as index builds or backfills, are not capped by these local metadata timeouts.
Migration startup incidents
If a new backend revision fails before /health is reachable, check the backend logs first. A migration metadata lock or statement timeout should name startup migrations as the failing phase.
If the worker starts but the backend does not, that is a useful signal. Workers use a read-only pending-migration check, while the backend is the runtime that applies SQL migrations.
To inspect blocking sessions in PostgreSQL, use a privileged database console and check locks around the migration metadata table:
SELECT a.pid, a.state, a.wait_event_type, l.mode, l.granted, left(a.query, 140) AS query
FROM pg_locks l
JOIN pg_class c ON c.oid = l.relation
JOIN pg_stat_activity a ON a.pid = l.pid
WHERE c.relname = 'schema_migrations';
SELECT pid, pg_blocking_pids(pid) AS blocked_by, state, left(query, 140) AS query
FROM pg_stat_activity
WHERE cardinality(pg_blocking_pids(pid)) > 0;If a stale session is clearly blocking a rollout, terminate only that backend session:
SELECT pg_terminate_backend(<pid>);Use this carefully. Confirm the session is stale or belongs to the failed rollout before terminating it. Restarting the database is the broader fallback when session-level recovery is not clear.
Worker incidents
If uploads succeed but new documents do not become searchable, treat it as a worker-path incident first.
Check in this order:
- The backend can accept uploads and write document records.
- The document worker is running and can connect to PostgreSQL.
- Source-file storage is reachable from the worker.
- The embedding provider credentials and model settings are valid.
- The queue is draining rather than growing.
- Failed jobs have enough logs to identify parser, storage, embedding, or timeout failures.
For local polling deployments, WORKER_DISPATCH_DRIVER=noop means the worker claims durable jobs from PostgreSQL. For queue-backed deployments, also check Cloud Tasks or AMQP delivery and retry configuration.
Secrets
Rotate these values deliberately and keep historical impact in mind:
SESSION_COOKIE_SECRETaffects browser sessions.WORKSPACE_TOKEN_SECRETaffects workspace API tokens.PUBLIC_CHAT_SESSION_SECRETaffects anonymous public-chat sessions and website embeds.CONNECTOR_ENCRYPTION_KEYprotects both connector secrets and per-workspace LLM provider API keys at rest. The value must be 32 random bytes, base64-encoded — generate one withopenssl rand -base64 32. The bootstrap command generates a key automatically when.envdoes not already have one. Workspaces that have stored an API key in the UI cannot read it back after the key is changed; operators must coordinate a rotation by clearing or re-entering each affected provider credential.- provider API keys affect chat, rewrite, rerank, and embedding calls.
Changing encryption or token secrets without a migration or rotation plan can invalidate existing sessions, tokens, or saved connector credentials.
Health checks
At minimum, monitor:
- backend
/health - frontend reachability
- worker process liveness
- PostgreSQL connection errors
- document-processing failures
- embedding provider failures
- disk or bucket storage errors
- HTTP 429 and 5xx rates for public chat and website embeds
The key point is that self-hosting is mostly database, storage, worker, and provider-key operations. The app itself is straightforward once those surfaces are backed up and monitored.
Read next
- Deployment — the production contract, required secrets, and rollout checklist this runbook assumes.
- Enterprise usage limits — cap indexed storage, monthly content, and monthly answers per account on Enterprise deployments.