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| Image | Base | Description |
|---|---|---|
almyty/api | node:24-alpine | Backend API server |
almyty/frontend | nginx:1.25-alpine | Frontend 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 -dThe frontend is available at http://localhost:4001 and the backend API at http://localhost:4000.
Services
| Service | Image | Port (host) | Port (container) |
|---|---|---|---|
postgres | postgres:16-alpine | 5433 | 5432 |
redis | redis:7-alpine | 6380 | 6379 |
backend | almyty/api | 4000 | 3000 |
frontend | almyty/frontend | 4001 | 3000 |
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 ./frontendBoth 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
| Overlay | Purpose |
|---|---|
development | Local k8s (minikube, kind). Single replicas, no TLS. |
staging | Pre-production. Managed Postgres, ENCRYPTION_KEY wired. |
production | Full 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/productionTLS
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
| Variable | Description | Example |
|---|---|---|
DATABASE_HOST | PostgreSQL hostname | localhost |
DATABASE_PORT | PostgreSQL port | 5432 |
DATABASE_USERNAME | PostgreSQL user | almyty |
DATABASE_PASSWORD | PostgreSQL password | — |
DATABASE_NAME | PostgreSQL database name | almyty |
DB_SSL | Enable SSL for managed databases | true or false |
REDIS_HOST | Redis hostname | localhost |
REDIS_PORT | Redis port | 6379 |
JWT_SECRET | Secret for signing JWT tokens | Random 64+ char string |
ENCRYPTION_KEY | AES-256 key for encrypting stored secrets — credentials and LLM provider API keys | Random 32-byte hex string |
Optional variables
| Variable | Description | Default |
|---|---|---|
PORT | Backend listen port | 3000 |
FRONTEND_URL | Frontend origin for CORS | http://localhost:3002 |
VITE_API_BASE_URL | API origin for the frontend (cross-domain deploys) | (same origin) |
METRICS_RETENTION_DAYS | How long usage metrics and request logs are kept before the retention sweep prunes them (0 disables pruning) | 90 |
MONITORING_STATS_WINDOW_SECONDS | Rolling window the live monitoring dashboard aggregates over | 300 |
DB_MIGRATIONS_RUN | Whether the API runs pending migrations on startup (see Database migrations) | true |
RATE_LIMIT_TTL | Rate-limit window in seconds | 60 |
RATE_LIMIT_MAX | Requests allowed per window | 100 |
BULL_REDIS_HOST | Separate Redis for BullMQ (if desired) | Falls back to REDIS_HOST |
MAIL_HOST | SMTP host for outbound email | — |
MAIL_PORT | SMTP port | 587 |
MAIL_USER | SMTP username | — |
MAIL_PASS | SMTP password | — |
OLLAMA_ALLOW_PRIVATE_URLS | Allow 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=trueSet 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:
| Endpoint | Purpose | Checks |
|---|---|---|
GET /health | Full health | Database, Redis, disk, memory |
GET /health/live | Liveness probe | Process is running |
GET /health/ready | Readiness probe | Database 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: 10Monitoring 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:
| Endpoint | Purpose | Alert when |
|---|---|---|
GET /health/ready | Readiness — 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/live | Liveness — process is up (does not check dependencies). | Use for restart detection, not paging. |
GET /health | Full 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 optionallySENTRY_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 thealmyty-secretssecret. - Frontend: set the
VITE_SENTRY_DSNbuild-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 -dReview the Database migrations note above before upgrading a clustered deployment.