Skip to main content

Self-Hosting

Running your own instance of News Dashboard via Docker, Docker Compose, or Helm.

Know your role

A deployment operator chooses a deployment method, supplies secrets and environment configuration, operates PostgreSQL and persistent storage, monitors health, and performs backups and upgrades. An application administrator signs in to manage users and review ingest operations, statistics, and analytics.

One person can hold both roles, but host or cluster access does not grant application administrator access. After deployment, continue with Administration and operations for the in-app controls.

Deployment options

OptionBest for
Docker ComposeSingle-node deployments using the published GHCR image.
Docker runSmall installations where you already manage PostgreSQL separately.
HelmKubernetes deployments with bundled or external PostgreSQL.

For local development, use the root docker-compose.yml. For production, use docker-compose.prod.yml or Helm; the development compose file contains insecure local defaults.

Production Compose quick start

  1. Copy .env.example to .env.

  2. Set strong values for SESSION_SECRET, BOOTSTRAP_ADMIN_USERNAME, BOOTSTRAP_ADMIN_PASSWORD, POSTGRES_PASSWORD, and NEO4J_PASSWORD.

  3. Start the stack:

    docker compose -f docker-compose.prod.yml up -d
  4. Verify health:

    curl http://localhost:8080/api/health

The production Compose stack includes a bundled Neo4j container and wires the app to it with NEO4J_*, so the knowledge graph is enabled as soon as the stack is up. Backfill existing entities after first start:

docker compose -f docker-compose.prod.yml exec news-dashboard \
news-dashboard graph-backfill --limit 250 --days 30
docker compose -f docker-compose.prod.yml exec news-dashboard \
news-dashboard graph-relationship-backfill --limit 50 --days 7

The application image is published as:

ghcr.io/lihor-hub/news-dashboard:latest
ghcr.io/lihor-hub/news-dashboard:v<version>
ghcr.io/lihor-hub/news-dashboard:<commit-sha>
ghcr.io/lihor-hub/news-dashboard@sha256:<digest>

Resolve and deploy the published sha256 digest in production instead of tracking a mutable tag.

Production Helm quick start

The production Helm contract terminates application TLS at the Ingress and keeps the application Service private:

(
set -euo pipefail
IMAGE_DIGEST="${IMAGE_DIGEST:?set IMAGE_DIGEST to sha256:<64 lowercase hex>}"
: "${SESSION_SECRET:?set SESSION_SECRET}"
: "${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}"
: "${POSTGRES_HOST_PATH:?set POSTGRES_HOST_PATH}"
source ./scripts/production-deploy-lib.sh
production_cutover_enabled || { echo "Ingress cutover is not enabled" >&2; exit 2; }
prepare_production_helm_secret_files

helm upgrade --install news-dashboard ./helm/news-dashboard \
--namespace news-dashboard --create-namespace \
--values ./helm/news-dashboard/values-production.yaml \
--set-string image.digest="${IMAGE_DIGEST}" \
--set-string postgresql.persistence.hostPath="$POSTGRES_HOST_PATH" \
--set-file app.auth.sessionSecret="$PRODUCTION_SESSION_SECRET_FILE" \
--set-file postgresql.password="$PRODUCTION_POSTGRES_PASSWORD_FILE"
)

Supply secrets and installation-specific persistence at runtime. Do not commit them to a values file or pass their values through Helm's --set arguments. The shared helper uses protected temporary files and removes them on exit. Private/custom endpoints require a policy-only strict JSON additional-egress values file. Use deploy/additional-egress-values.example.json as the shape and persist it through ADDITIONAL_EGRESS_VALUES_FILE for manual deploys or the production GitHub environment variable ADDITIONAL_EGRESS_VALUES for CI. Never put credentials in this non-secret NetworkPolicy input. YAML-only syntax, aliases, merge keys, comments, and multiple documents are rejected. Before public cutover, verify the ClusterIP Service, TLS Ingress, backups and restore, rollback revision, and the existing /keycloak route. Caddy cannot share ports 80 and 443 with the ingress controller.

The live appliance procedure requires human access and is tracked in issue #1302. Follow Ingress HTTPS and Caddy migration for the staged verification and backend-first rollback order. Leave INGRESS_CUTOVER_ENABLED unset until that procedure is ready; the main workflow will publish and scan without touching the live release.

Required configuration

News Dashboard uses PostgreSQL at runtime. Configure either DATABASE_URL or the split POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DB, POSTGRES_USER, and POSTGRES_PASSWORD variables.

At minimum, a production instance also needs:

VariablePurpose
SESSION_SECRETSigns sessions and, unless overridden, digest mark-read tokens.
BOOTSTRAP_ADMIN_USERNAMEFirst admin username when no users exist.
BOOTSTRAP_ADMIN_PASSWORDFirst admin password when no users exist.
POSTGRES_PASSWORDPassword for the bundled or configured PostgreSQL user.
NEO4J_PASSWORDPassword for the bundled Neo4j graph store.

See Configuration for authentication, HTTPS, backup, and integration guides. The root README Configuration section is the canonical environment-variable reference.

Operations

  • Use /api/live for liveness checks; it does not require database access.
  • Use /api/ready for readiness checks; it verifies database connectivity.
  • Use /api/health for load-balancer or manual health checks.
  • Enable /metrics with METRICS_ENABLED=true only when you want Prometheus exposition.
  • Enable /docs, /redoc, and /openapi.json with ENABLE_API_DOCS=true only in trusted environments.

To upgrade a Compose deployment, pull the pinned replacement image and restart:

docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d

To roll back, set the image tag to the previous known-good version and run the same pull/up commands.