Skip to Content
Self-Hosting

Self-Hosting

almyty can be self-hosted using Docker Compose for single-server deployments or Kubernetes with Kustomize for production clusters.

Docker Hub

Official images are published to Docker Hub:

docker pull almyty/api:latest docker pull almyty/frontend:latest
ImageBaseDescription
almyty/apinode:24-alpineBackend API server
almyty/frontendnginx:1.25-alpineFrontend SPA served via nginx

Tags: latest (most recent master build), vX.Y.Z (releases), sha-XXXXXXX (per-commit).

Docker Compose

The repository includes a docker-compose.yml that runs Postgres, Redis, the backend API, and the frontend (with an optional nginx profile for production):

services: postgres: image: postgres:16-alpine environment: POSTGRES_DB: almyty POSTGRES_USER: postgres POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} ports: ["5433:5432"] volumes: [postgres_data:/var/lib/postgresql/data] redis: image: redis:7-alpine ports: ["6380:6379"] command: redis-server --appendonly yes backend: build: { context: ./backend, target: development } ports: ["4000:3000"] environment: - DATABASE_HOST=postgres - REDIS_HOST=redis - JWT_SECRET=${JWT_SECRET:-dev-secret-change-me} - OPENAI_API_KEY=${OPENAI_API_KEY:-} - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-} depends_on: postgres: { condition: service_healthy } redis: { condition: service_started } frontend: build: { context: ./frontend } ports: ["4001:3000"] depends_on: [backend] volumes: postgres_data: redis_data:

Quick start

git clone https://github.com/almyty-inc/almyty.git cd almyty cp .env.example .env # edit with your values docker-compose up -d

The frontend is available at http://localhost:4001 and the backend API at http://localhost:4000.

Services

ServiceImagePort (host)Port (container)
postgrespostgres:16-alpine54335432
redisredis:7-alpine63806379
backendalmyty/api40003000
frontendalmyty/frontend40013000

Building from source

If you need custom builds instead of pulling from Docker Hub:

docker build -t almyty/api ./backend docker build -t almyty/frontend ./frontend

Both Dockerfiles are multi-stage. The backend builds on node:24-alpine and produces a minimal production image. The frontend builds the Vite app on node:24-alpine and copies the output into nginx:1.25-alpine.

Kubernetes

The repository provides Kustomize base manifests with three overlays.

Overlays

OverlayPurpose
developmentLocal k8s (minikube, kind). Single replicas, no TLS.
stagingPre-production. Managed Postgres, ENCRYPTION_KEY wired.
productionFull production. Multiple replicas, TLS via cert-manager, resource limits.

Deploy

# Development kubectl apply -k k8s/overlays/development # Staging kubectl apply -k k8s/overlays/staging # Production kubectl apply -k k8s/overlays/production

TLS

The production overlay includes a cert-manager ClusterIssuer for Let’s Encrypt. Set DOMAIN in the overlay’s configmap to your domain. Certificates are provisioned automatically.

Required environment variables

VariableDescriptionExample
DATABASE_HOSTPostgreSQL hostnamelocalhost
DATABASE_PORTPostgreSQL port5432
DATABASE_USERNAMEPostgreSQL useralmyty
DATABASE_PASSWORDPostgreSQL password
DATABASE_NAMEPostgreSQL database namealmyty
DB_SSLEnable SSL for managed databasestrue or false
REDIS_HOSTRedis hostnamelocalhost
REDIS_PORTRedis port6379
JWT_SECRETSecret for signing JWT tokensRandom 64+ char string
ENCRYPTION_KEYAES-256 key for encrypting stored secrets — credentials and LLM provider API keysRandom 32-byte hex string

Optional variables

VariableDescriptionDefault
PORTBackend listen port3000
FRONTEND_URLFrontend origin for CORShttp://localhost:3002
VITE_API_BASE_URLAPI origin for the frontend (cross-domain deploys)(same origin)
METRICS_RETENTION_DAYSHow long usage metrics and request logs are kept before the retention sweep prunes them (0 disables pruning)90
MONITORING_STATS_WINDOW_SECONDSRolling window the live monitoring dashboard aggregates over300
DB_MIGRATIONS_RUNWhether the API runs pending migrations on startup (see Database migrations)true
RATE_LIMIT_TTLRate-limit window in seconds60
RATE_LIMIT_MAXRequests allowed per window100
BULL_REDIS_HOSTSeparate Redis for BullMQ (if desired)Falls back to REDIS_HOST
MAIL_HOSTSMTP host for outbound email
MAIL_PORTSMTP port587
MAIL_USERSMTP username
MAIL_PASSSMTP password
OLLAMA_ALLOW_PRIVATE_URLSAllow Ollama providers to target localhost/private-network URLs (see Local models with Ollama)false

Local models with Ollama

almyty’s SSRF protection refuses private and loopback URLs on outbound LLM calls — the right default for a multi-tenant host, but it also blocks a machine-local Ollama  server. Self-hosted deployments can opt out for the ollama provider type only:

OLLAMA_ALLOW_PRIVATE_URLS=true

Set it on the backend API process, then add an Ollama provider (Models → Add Provider → Ollama). The URL defaults to http://localhost:11434; chat and tool calling use Ollama’s OpenAI-compatible /v1 endpoint, the model list comes from /api/tags, and memory embeddings use /api/embed. No API key is needed.

Networking note for Docker Compose: inside the backend container, localhost is the container itself, not your machine. Point the provider at the host instead — http://host.docker.internal:11434 (on Linux, add extra_hosts: ["host.docker.internal:host-gateway"] to the backend service). The escape hatch only relaxes the private-range ban: URLs must still be http(s) with no embedded credentials, and every other provider type keeps the full SSRF gate.

Health check endpoints

The backend exposes three health endpoints:

EndpointPurposeChecks
GET /healthFull healthDatabase, Redis, disk, memory
GET /health/liveLiveness probeProcess is running
GET /health/readyReadiness probeDatabase and Redis are reachable

Kubernetes probes

livenessProbe: httpGet: path: /health/live port: 3000 initialDelaySeconds: 10 periodSeconds: 15 readinessProbe: httpGet: path: /health/ready port: 3000 initialDelaySeconds: 5 periodSeconds: 10

Monitoring and observability

Kubernetes probes keep a single deployment healthy, but they don’t tell you when something breaks. Wire up external monitoring so failures page you instead of being discovered by hand.

Uptime checks

External uptime pingers (UptimeRobot, Pingdom, Better Uptime, or a Kubernetes-native probe exporter) can’t be provisioned from this repo — configure them in your monitoring tool of choice against these endpoints:

EndpointPurposeAlert when
GET /health/readyReadiness — API plus its Postgres and Redis dependencies. This is the one to page on.Non-200, or down for more than one check interval.
GET /health/liveLiveness — process is up (does not check dependencies).Use for restart detection, not paging.
GET /healthFull health report (all indicators).Useful for a status dashboard.

Recommended baseline: poll /health/ready every 30–60s from at least two regions, alert on two consecutive failures. /health/ready returns non-200 when the database or Redis is unreachable, so it catches the dependency outages that a bare liveness check would miss.

Deploy smoke gate

The GitHub Actions deploy workflow runs a one-off migration Job to completion and only rolls out new pods if it succeeds (see Database migrations below). A failed migration stops the deploy instead of shipping pods against a schema they don’t expect — so a broken release surfaces at deploy time, not in production.

Error tracking (Sentry)

almyty ships with optional Sentry  integration on both the API and the frontend. It is off by default and a complete no-op until you configure a DSN — no client loads, no network calls, no behavior change. Turn it on to have unhandled errors and 5xx responses surface in Sentry instead of buried in logs.

  • Backend: set SENTRY_DSN (and optionally SENTRY_ENVIRONMENT, e.g. production / staging) on the API. The global exception filter reports 5xx responses and unhandled exceptions; client errors (4xx) are never sent. In Kubernetes these are optional keys on the almyty-secrets secret.
  • Frontend: set the VITE_SENTRY_DSN build-arg when building the frontend image. It captures unhandled errors and errors caught by the app’s error boundary. The environment tag is inferred from the host (development stays untracked).

With no DSN set, both stay dark, so you can adopt error tracking without changing anything else about your deployment.

Database migrations

How migrations run depends on how you deploy.

On a single-node or Docker Compose setup the API applies any pending migrations itself when it starts (DB_MIGRATIONS_RUN defaults to true). There’s only one process, so there’s nothing to coordinate.

In a cluster you don’t want several replicas racing to run the same migrations as they boot. Set DB_MIGRATIONS_RUN=false and run migrations once, before the new pods roll out — as a Kubernetes Job, an init container, or npm run typeorm migration:run. The provided GitHub Actions deploy workflow already does this: it runs a one-off migration Job to completion and only continues the rollout if it succeeds, so a failed migration stops the deploy instead of shipping pods against a schema they don’t expect.

Either way, back up the database before upgrading.

Rate limiting

Rate limits are backed by Redis, so the configured limit (RATE_LIMIT_MAX per RATE_LIMIT_TTL seconds) is shared across every API replica rather than counted per process. Point all replicas at the same Redis and the limit holds for the cluster as a whole.

Compliance and data residency

Self-hosting keeps every byte — conversations, memory, credentials, logs — in infrastructure you control, which is the simplest answer to GDPR data-residency questions. Secrets are AES-256-GCM encrypted with your ENCRYPTION_KEY; rotate it like any other production secret and never commit it. Remember that BYOK means conversation content flows to your model providers under your API keys, so your data-processing agreements with those providers are part of your deployment’s compliance story.

The Compliance page maps platform features to the EU AI Act (transparency disclosure, human oversight, record-keeping, technical documentation export) and the other regimes that typically apply. The repository’s SECURITY.md documents the vulnerability-disclosure process and how to generate an SBOM for your release.

Upgrades

docker-compose pull docker-compose up -d

Review the Database migrations note above before upgrading a clustered deployment.