NewRelease 0.12.3 is out: Engine v0.12.3, Dashboard 0.5.2, Helm 0.12.3, the four SDKs, CLI 0.8.2 and MCP 0.12.3Release 0.12.3 is outSee what's new →

Deployment

This guide is for operators running the self-hosted Dakera server in production. It covers the supported ways to run v0.12.3 (Docker, Docker Compose, Kubernetes with Helm, and systemd), then what every deployment needs: TLS termination, the data volume, health probes, backups, monitoring, upgrades and rollback.

Every setting is an environment variable; the complete list is in the Configuration reference.

v0.12.3 — Upgrading from v0.11.108, v0.12.0, v0.12.1 or v0.12.2? Read Upgrading to v0.12 first (gRPC keys, permissions, enforced quotas, probes; from v0.12.1 every cluster node needs DAKERA_CLUSTER_SECRET; from v0.12.2 sessions end after 4 hours idle by default and writes are validated more strictly; from v0.12.3 the images have no curl, so health checks use dakera healthcheck). To go back, see Rolling back to v0.11. Pin a version with ghcr.io/dakera-ai/dakera:0.12.3.

Docker (single node)

docker run -d \
  --name dakera \
  --restart unless-stopped \
  -p 3000:3000 \
  -e DAKERA_PORT=3000 \
  -e DAKERA_ROOT_API_KEY=your-secret \
  -e DAKERA_STORAGE=filesystem \
  -e DAKERA_STORAGE_PATH=/data \
  -v dakera-data:/data \
  -v dakera-models:/app/models \
  ghcr.io/dakera-ai/dakera:0.12.3

/data is the data root (write-ahead log, tiers, knowledge graph, filesystem storage): mount one volume there. /app/models holds only models the image does not ship, so you can leave it out with the default models. Authentication is on by default; send the key as Authorization: Bearer or X-API-Key. Add -p 50051:50051 for gRPC, which requires an API key.

The image ships its default models (bge-large and the bge-reranker-v2-m3 reranker) already converted, so a default start needs no network, downloads nothing and works with a read-only root filesystem. See Model store and air-gapped installs below.

The recommended way to run a single production node. This compose file runs Dakera on S3 storage provided by MinIO; a one-off minio-setup service creates the bucket, because the server checks at startup that the bucket exists and refuses to start without it.

# docker-compose.yml
services:
  dakera:
    image: ghcr.io/dakera-ai/dakera:0.12.3
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      DAKERA_PORT: 3000
      DAKERA_ROOT_API_KEY: ${DAKERA_ROOT_API_KEY}
      DAKERA_STORAGE: s3
      DAKERA_S3_ENDPOINT: http://minio:9000
      DAKERA_S3_BUCKET: dakera
      DAKERA_S3_REGION: us-east-1
      AWS_ACCESS_KEY_ID: ${MINIO_ROOT_USER}
      AWS_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD}
      DAKERA_ENCRYPTION_KEY: ${DAKERA_ENCRYPTION_KEY}
      RUST_LOG: info
    volumes:
      - dakera-data:/data
    depends_on:
      minio-setup:
        condition: service_completed_successfully
    healthcheck:
      test: ["CMD", "dakera", "healthcheck", "/health/ready"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10m

  minio:
    image: minio/minio:latest
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    volumes:
      - minio-data:/data
    healthcheck:
      test: ["CMD", "mc", "ready", "local"]
      interval: 10s
      timeout: 5s
      retries: 5

  minio-setup:
    image: minio/mc:latest
    depends_on:
      minio:
        condition: service_healthy
    environment:
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    entrypoint: >
      /bin/sh -c "mc alias set local http://minio:9000 $$MINIO_ROOT_USER $$MINIO_ROOT_PASSWORD
      && mc mb --ignore-existing local/dakera"

volumes:
  dakera-data:
  minio-data:
# Generate secrets and start
printf "DAKERA_ROOT_API_KEY=%s\nDAKERA_ENCRYPTION_KEY=%s\nMINIO_ROOT_USER=minioadmin\nMINIO_ROOT_PASSWORD=%s\n" \
  "$(openssl rand -hex 32)" "$(openssl rand -hex 32)" "$(openssl rand -hex 16)" > .env

docker compose up -d

Kubernetes

helm install dakera oci://ghcr.io/dakera-ai/dakera-helm/dakera \
  --version 0.12.3 --namespace dakera --create-namespace \
  --set dakera.rootApiKey="$(openssl rand -hex 32)" \
  --set minio.rootPassword="$(openssl rand -hex 16)"

Chart 0.12.3 deploys server 0.12.3 and Dashboard 0.5.2 (pin with --version 0.12.3). From 0.12.3 the server runs with a read-only root filesystem by default (dakera.readOnlyRootFilesystem) and only Dakera pods may reach the built-in MinIO (minio.networkPolicy). Settings without a dedicated value go through dakera.extraEnv; models can be pulled by an init container before the pod serves:

# values.yaml
dakera:
  extraEnv:
    - name: DAKERA_MODEL
      value: bge-m3
  models:
    pull: [configured]        # init container runs dakera models pull
    persistence:
      enabled: true           # keep the model cache across pods
KeyDefaultDescription
dakera.config.dataRoot/dataDAKERA_STORAGE_PATH; the persistence volume (dakera.persistence.size, 20Gi) is mounted here
dakera.podSecurityContextuid/gid 1000, fsGroup: 1000Makes the mounted volumes writable by the image's user; set {} where the platform assigns uids (OpenShift)
dakera.readOnlyRootFilesystemfalseRead-only root filesystem (data root, model cache and /tmp are volumes)
dakera.cluster.secretemptyDAKERA_CLUSTER_SECRET (or dakera.cluster.existingSecret); required on every node in cluster mode from v0.12.1
dakera.autoscaling.enabledfalseA server locks its data root, so one release is one node; enable only with persistence.enabled=false and an external S3
rollback.enabledfalseRenders the one-off dakera downgrade Job

Full values: charts/dakera/values.yaml · ArtifactHub. Kustomize manifests available in dakera-deploy/k8s/.

Reverse proxy & TLS termination

Never expose Dakera directly to the internet. Always terminate TLS at a reverse proxy. Dakera runs as a single binary without built-in TLS — this keeps the deployment surface minimal and lets you use your preferred certificate management.

Nginx

# /etc/nginx/sites-available/dakera
server {
    listen 443 ssl http2;
    server_name dakera.example.com;

    ssl_certificate     /etc/letsencrypt/live/dakera.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dakera.example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # SSE support (required for event streaming)
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 86400s;
    }
}

# Redirect HTTP → HTTPS
server {
    listen 80;
    server_name dakera.example.com;
    return 301 https://$host$request_uri;
}
# Obtain certificate with certbot
sudo certbot --nginx -d dakera.example.com

Caddy

# Caddyfile — automatic HTTPS with Let's Encrypt
dakera.example.com {
    reverse_proxy localhost:3000 {
        flush_interval -1   # required for SSE streaming
    }
}

Caddy automatically provisions and renews TLS certificates via Let's Encrypt — no additional setup required.

Self-hosted VPS

Run the Dakera binary directly on a Linux host (for example Ubuntu or Debian) under systemd. This suits a single node on dedicated hardware. With WorkingDirectory=/opt/dakera and no DAKERA_STORAGE_PATH, the data root is /opt/dakera/data.

# /etc/systemd/system/dakera.service
[Unit]
Description=Dakera Agent Memory Engine
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=dakera
Group=dakera
WorkingDirectory=/opt/dakera
ExecStart=/opt/dakera/dakera
Restart=always
RestartSec=5
LimitNOFILE=65535

# Environment
EnvironmentFile=/opt/dakera/.env

[Install]
WantedBy=multi-user.target
# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable dakera
sudo systemctl start dakera

# Check status
sudo systemctl status dakera
curl http://localhost:3000/health

Cloud deployment

PlatformServiceQuick start
AWSECS FargateTask definition with ghcr.io/dakera-ai/dakera:latest, EFS volume for /data, ALB for TLS
GCPCloud Rungcloud run deploy dakera --image ghcr.io/dakera-ai/dakera:latest --port 3000
AzureContainer Instancesaz container create --image ghcr.io/dakera-ai/dakera:latest --ports 3000
Fly.iofly launchfly launch --image ghcr.io/dakera-ai/dakera:latest with persistent volume
RailwayDocker image serviceDeploy ghcr.io/dakera-ai/dakera:latest with a volume mounted at /data

Resource sizing

Size from your own data and load: there is no published table by memory count. These are the figures measured for v0.12.0, with where they were taken.

WhatMeasuredWhere
HNSW memory per 1024-d vector2.76 KB (4.81 KB in v0.11.108), recall@10 unchangedBEIR Quora, 50k vectors
Container memory after reranking0.65 GB, flat across top_k 1 to 32 (4.98 GB in v0.11.108)4 CPU / 8 GiB container, bge-reranker-v2-m3
Recall with reranking, top_k 166.2 s, 24 CPU-s (11.5 s, 45 CPU-s in v0.11.108)CPU-only, 4 vCPU / 8 GiB container, bge-reranker-v2-m3 (568M-parameter cross-encoder), compared with v0.11.108 on the same machine; GPU or hosted rerankers are a different deployment class
Memory, ingest and recall p50 of a three-conversation run0.97 GB, 46 s, 1.8 s (2.4 GB, 124 s, 3.8 s in v0.11.108)same workload on v0.11.108 and v0.12.0
All multimodal models loaded530 MiB process heap (excluding memory-mapped model files)multimodal footprint run
Default image783 MB (CPU, amd64), ready 4.4 s after startno download
Re-embed after upgrading from v0.11about 35 minutes per 10,000 memories on bge-large; recall is served throughoutconversational turns
Late-interaction recall p500.10 s at 1k memories, 0.23 s at 10ksteady state, colbert-small
Image indexingabout 10.7 s per pageCPU

Still being measured: the 50k capacity re-run, the embedder benchmark, the load sweep and durable writes per second. Media jobs reserve their working memory before they start and answer 503 with Retry-After when it does not fit. RocksDB (the tiered hot tier) benefits from high random-read IOPS; use SSD or NVMe. DAKERA_L1_CACHE_SIZE takes a vector count or a byte size such as 512MB.

Data root and volumes

Every local path derives from one data root, DAKERA_STORAGE_PATH (default ./data; the images set /data and declare it, with /app/models, as volumes). Before v0.12 the image's root was /app/data: a server that finds data there and none under /data keeps using it where it is and warns, naming the variable that pins it (data_location in /health config_warnings). Nothing is moved. Keep the whole data root on a persistent volume, also with S3 storage: it holds the knowledge graph and audit events.

PathHolds
{root}/wal, {root}/wal-snapshotsWrite-ahead log and snapshots
{root}/hotRocksDB hot tier
{root}/cache/warmWarm tier
{root}/cluster-outboxDurable replication outbox (cluster mode)
{root}/cluster-node-idCluster node id, generated once when DAKERA_CLUSTER_NODE_ID is unset
{root}/.instance_idTelemetry instance id (see Telemetry)
{root}/.dakera.lockHeld while a server runs: a second server on the same root refuses to start

DAKERA_ROCKSDB_PATH, DAKERA_CACHE_DIR, DAKERA_WAL_DIR, DAKERA_WAL_SNAPSHOT_DIR and DAKERA_GRAPH_PATH pin each path; DAKERA_DATA_DIR is an alias of DAKERA_STORAGE_PATH (two different values are an invalid value). Data at a pre-0.12 location is used where it is, with a warning.

Health checks

Since v0.12 the REST port is bound before models load, so a model that has to download no longer sits behind a closed port (a liveness probe used to restart the process, which downloaded again from zero). Use the two probe routes:

RouteAnswersUse for
GET /health/live200 as soon as the port is boundLiveness
GET /health/ready200 once the embedding model is loaded and storage answers; 503 with what is loading and per-file download progress before thatReadiness
GET /healthstatus (healthy or degraded), version; lists degraded components and config_warnings; 503 with "status": "starting" while models loadHumans and dashboards

Every other request answers 503 with Retry-After: 5 until the models are loaded. The reranker, and whisper and the vision model when enabled, load in the background and do not gate readiness.

curl http://localhost:3000/health/ready
# {"ready":true,"version":"0.12.3","checks":{"storage":{"status":"ok","message":null},"embedding_engine":{...},"tiered_engine":{...}}}

Docker

The published images already carry a HEALTHCHECK with a 10-minute start period: dakera healthcheck, a probe built into the server binary (the images ship without curl from v0.12.3). It connects to the node's own REST port, requests the path you give it (default /health) and exits 0 only on 200. For your own compose file:

healthcheck:
  test: ["CMD", "dakera", "healthcheck", "/health/ready"]
  interval: 30s
  timeout: 5s
  retries: 3
  start_period: 10m

Kubernetes

# In pod spec — the startup probe covers port binding (write-ahead-log replay)
startupProbe:
  httpGet:
    path: /health/live
    port: 3000
  periodSeconds: 10
  failureThreshold: 60
livenessProbe:
  httpGet:
    path: /health/live
    port: 3000
  periodSeconds: 30
readinessProbe:
  httpGet:
    path: /health/ready
    port: 3000
  periodSeconds: 10

The Helm chart (dakera-helm) is already set up this way (liveness on /health/live behind a 10-minute startup probe, readiness on /health/ready) and sets a pod security context of uid/gid 1000 with fsGroup: 1000.

Model store and air-gapped installs

Models other than the shipped defaults (another embedding model such as bge-m3, whisper, the vision model, GLiNER entity extraction) download on first use into the model cache, HF_HOME (/app/models in the images, a VOLUME). Mount a volume there to download each model once. Every file is checked against a pinned SHA-256. Downloads honour HTTPS_PROXY, HTTP_PROXY, ALL_PROXY, NO_PROXY, HF_ENDPOINT (a mirror) and HF_HUB_OFFLINE.

# What is on disk, and what can go
docker exec dakera dakera models list
docker exec dakera dakera models prune --dry-run

# Fetch a model ahead of the first start
docker run --rm -v dakera-models:/app/models ghcr.io/dakera-ai/dakera:0.12.3 models pull bge-m3

# Air-gapped: seed on a connected machine, copy to the host, mount at /app/models
docker run --rm -v "$PWD/models:/seed" ghcr.io/dakera-ai/dakera:0.12.3 models pull --dir /seed bge-m3 whisper vision
# ...then run with -v ./models:/app/models -e HF_HUB_OFFLINE=1
The default image needs no model volume at all. A volume kept from v0.11 holds copies of the image's models that v0.12 no longer reads; dakera models prune frees them, but do not prune until you are staying on v0.12 — v0.11.108 reads those files. The full guide, with proxies, mirrors, Helm and derived images, is Models and Docker.

Backup & restore

v0.12.0 — Backup download, upload and restore need a key of global super_admin scope (a key pinned to namespaces, or an unrestricted admin key, gets 403). A stored, enabled backup schedule now runs; the live schedule is no longer replaced by a restore. Backups taken under v0.12 are restored by v0.12 and then downgraded — see Rolling back to v0.11.

Dakera provides backup and restore through the admin API. A backup holds the namespaces' records with their sidecars, attachments and knowledge-graph edges, the API-key records and the encryption keyring; text is kept sealed (a backup never holds plaintext _text). Creating, listing and polling need global admin; download, upload and restore need global super_admin.

# Create a backup (202; the backup runs in the background)
curl -X POST http://localhost:3000/admin/backups \
  -H "Authorization: Bearer $DAKERA_ROOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"before-upgrade","compression":"zstd","encrypt":false}'
# {"backup":{"backup_id":"...","name":"before-upgrade","status":"...", ...}}

# Poll it, or list all
curl http://localhost:3000/admin/backups/BACKUP_ID -H "Authorization: Bearer $DAKERA_ROOT_API_KEY"
curl http://localhost:3000/admin/backups -H "Authorization: Bearer $DAKERA_ROOT_API_KEY"

# Download a completed backup (a gzip-compressed JSON bundle)
curl -o backup.json.gz \
  http://localhost:3000/admin/backups/BACKUP_ID/download \
  -H "Authorization: Bearer $DAKERA_ROOT_API_KEY"

# Restore a stored backup (202 with a restore_id; add "overwrite": true for the exact point in time)
curl -X POST http://localhost:3000/admin/backups/restore \
  -H "Authorization: Bearer $DAKERA_ROOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"backup_id":"BACKUP_ID","overwrite":false}'

# Restore from a downloaded bundle (raw gzip body; needs S3 storage)
curl -X POST http://localhost:3000/admin/backups/upload \
  -H "Authorization: Bearer $DAKERA_ROOT_API_KEY" \
  --data-binary @backup.json.gz

Scheduled backups

# Daily at 02:00, 30-day retention, zstd
curl -X POST http://localhost:3000/admin/backups/schedule \
  -H "Authorization: Bearer $DAKERA_ROOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true,"cron":"0 2 * * *","retention_days":30,"max_backups":30,"compression":"zstd"}'

Since v0.12 a stored, enabled schedule runs: one backup per due slot, on the leader in a cluster; retention_days and max_backups apply to scheduled backups only. An upgraded deployment with an enabled stored schedule gets a backup_schedule configuration warning at its first start.

Recommended strategy: Daily full backups with 30-day retention. For enterprise deployments, replicate backups to a separate S3 bucket in a different region for disaster recovery.

Monitoring & observability

Dakera exposes Prometheus metrics at GET /metrics (no key; scrape it from inside your network and do not expose it publicly). GET /v1/ops/metrics returns the same exposition behind a key with global admin scope. The names below are emitted by the v0.12.0 server.

MetricTypeDescription
dakera_http_requests_total{method,path,status}CounterHTTP requests; refusals (429, 401/403, 408, 413, 503) and unmatched paths (path="unmatched") are counted
dakera_http_request_duration_secondsHistogramRequest latency
dakera_active_requestsGaugeIn-flight requests
dakera_query_latency_secondsHistogramVector query latency
dakera_memory_store_total, dakera_memory_recall_total, dakera_memory_forget_totalCountersMemory operations by agent and namespace
dakera_memories_total{agent_id}GaugeLive memory count per agent
dakera_recall_pipeline_stage_duration_secondsHistogramPer-stage recall latency
dakera_inference_duration_secondsHistogramEmbedding latency
dakera_errors_total{type,operation}CounterErrors; memory_budget and memory_backpressure are memory refusals
dakera_component_degraded{component}Gauge1 while a component is listed under degraded in /health
dakera_config_warningsGaugeNumber of config_warnings in /health
dakera_model_loads_total, dakera_model_load_duration_secondsCounter, histogramLazy model loads by model and outcome (download included)
dakera_memory_budget_reserved_bytesGaugeWorking memory reserved by media jobs and entity extraction
dakera_backups_total{outcome}CounterBackups by outcome (completed / failed)
dakera_wal_write_failures_totalCounterWrite-ahead-log appends that failed
dakera_memory_read_failures_totalCounterMemories a read path skipped because their content did not open
dakera_reembed_migration_remainingGaugeMemories the post-upgrade re-embed still has to process
dakera_encryption_*, dakera_tiered_*, dakera_rocksdb_*, dakera_cluster_*variousKeyring and re-seal progress; tiered storage; RocksDB; replication (outbox, tombstones, dakera_cluster_clock_ahead_ms)
dakera_process_resident_bytesGaugeProcess memory

Prometheus scrape config

# prometheus.yml
scrape_configs:
  - job_name: dakera
    scrape_interval: 15s
    static_configs:
      - targets: ["dakera:3000"]
    metrics_path: /metrics

Alert recommendations

AlertConditionSeverity
Health check failingup{job="dakera"} == 0Critical
5xx ratesum(rate(dakera_http_requests_total{status=~"5.."}[5m])) / sum(rate(dakera_http_requests_total[5m])) > 0.05Warning
Shedding loadsum(rate(dakera_http_requests_total{status="503"}[5m])) / sum(rate(dakera_http_requests_total[5m])) > 0.02Warning
Component degradedmax by (component) (dakera_component_degraded) == 1Warning
Configuration warningsdakera_config_warnings > 0Warning
Backup failedincrease(dakera_backups_total{outcome="failed"}[1d]) > 0Warning
Write-ahead-log failuresincrease(dakera_wal_write_failures_total[15m]) > 0Critical

OpenTelemetry tracing

# Enable OTLP trace export
DAKERA_TRACING_ENABLED=true
OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4317
OTEL_SERVICE_NAME=dakera

Telemetry

The self-hosted engine sends product telemetry to Dakera's PostHog project (EU) to show which versions, platforms and storage setups are in use, whether engines stay up and how often they are upgraded. It never reads or sends memory content, embeddings, queries, ids, names, API keys, file paths or URLs.

Not anonymous at the network level (v0.12) — engine_started and engine_heartbeat carry the machine's hostname, and PostHog records the connecting IP address and derives a coarse location from it (GeoIP: country, region, city). Events: engine_started, engine_stopped, engine_crashed, engine_version_changed, and a heartbeat every 6 hours (v0.11: 24 hours). Everything sent is listed in Telemetry.

Opt out

# Either variable turns telemetry off completely — nothing is counted, written or sent
DAKERA_TELEMETRY=0
DO_NOT_TRACK=1
VariableDefaultDescription
DAKERA_TELEMETRYon0, false, off or no disables telemetry
DO_NOT_TRACK—1 or true disables telemetry
DAKERA_TELEMETRY_DEBUGoffLog every batch (as JSON) before it is sent

Log management

Dakera writes its logs to standard output; RUST_LOG sets the filter (the images set info). Audit records are one line per request, JSON by default (DAKERA_AUDIT_FORMAT).

# Log levels
RUST_LOG=info                          # production (recommended)
RUST_LOG=debug                         # development / troubleshooting
RUST_LOG=info,tower_http=debug         # verbose HTTP logging only

Log retention

Rotate logs where they are collected. Under systemd they go to the journal (journalctl -u dakera; retention is set in journald.conf). Under Docker, set the logging driver's rotation:

# docker-compose.yml, on the dakera service
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "5"

Upgrading

v0.12.3 upgrades in place from v0.11.108, v0.12.0, v0.12.1 or v0.12.2 on the same data and environment, and goes back to v0.11.108 with one command (dakera downgrade). Read Upgrading to v0.12 before you start — it lists what changes on purpose. In short:

  1. Take a backup: POST /admin/backups.
  2. Run the new image's dakera --check-config against the deployment's environment and data volume; fix or note each warning.
  3. Give gRPC clients an API key; check namespace-pinned keys and backup tooling against the new permissions (from v0.11.108).
  4. Cluster: set the same DAKERA_CLUSTER_SECRET on every node and upgrade the nodes in one window (see High Availability).
  5. Sessions: from v0.12.2 the server ends sessions idle for 4 hours (DAKERA_SESSION_IDLE_TIMEOUT_SECS; 0 keeps them open until a client ends them). Check clients that write reserved metadata keys or over-size content against the v0.12.2 checks.
  6. Health checks and admin tooling: from v0.12.3 probe with dakera healthcheck instead of curl inside the container, give tooling that calls POST /ops/shutdown a super_admin key, and set DAKERA_AUTOPILOT_DEDUP_MODE=merge if you rely on automatic deduplication (v0.12.3 checks).
  7. Stop the running version and start v0.12.3 on the same data and environment.
  8. Nothing to do for the full-text indexes: v0.12.3 re-analyses indexes built by v0.11.108 in the background while they serve. (On v0.12.0 this was a manual step; see Full-text indexes.)
  9. Watch /health: config_warnings, degraded, embed_migration.
# 1. Pull the new image
docker pull ghcr.io/dakera-ai/dakera:0.12.3

# 2. Restart the container on the same volumes and environment
docker compose down && docker compose up -d

# 3. Wait for readiness and verify the version
curl http://localhost:3000/health/ready
# {"ready":true,"version":"0.12.3","checks":{...}}
curl http://localhost:3000/health
# {"service":"dakera","status":"healthy","version":"0.12.3","degraded":[],"config_warnings":[], ...}

For systemd deployments: replace the binary at /opt/dakera/dakera, then sudo systemctl restart dakera. Always verify the readiness endpoint after upgrade. Clusters that already run v0.12.0, v0.12.1 or v0.12.2 with DAKERA_CLUSTER_SECRET upgrade node by node; otherwise set the secret and switch every node in one window (see High Availability). After an upgrade from v0.11.108, stored memories that need it are re-embedded once in the background (about 35 minutes per 10,000 memories on bge-large; nothing to do for minilm, gte-modernbert-base, bge-m3 or colbert-small); recall is served throughout.

Rolling back

Stop v0.12, run dakera downgrade once with the server's environment and volumes (docker run --rm <env and volumes> ghcr.io/dakera-ai/dakera:0.12.3 downgrade), read its JSON report (exit 0: the data is v0.11.108's; 1: run it again; 78: it refused and changed nothing), then start v0.11.108 (from v0.12.2 the downgrade also rewrites binary RocksDB hot-tier records as JSON). Going back to v0.12.2, v0.12.1 or v0.12.0 needs no downgrade. Details and what blocks a rollback: Rolling back to v0.11.

Production checklist

ItemPriorityNotes
Set DAKERA_ROOT_API_KEYRequiredUse a cryptographically random 32+ char secret
TLS via reverse proxyRequiredNginx or Caddy in front — never expose port 3000 directly
Persistent volumeRequiredSSD-backed storage for DAKERA_STORAGE_PATH
Object storageRecommendedMinIO or AWS S3 for durability and the cold tier
Encryption at restRecommendedSet DAKERA_ENCRYPTION_KEY for AES-256-GCM
Scrape /metricsRecommendedPrometheus with alert rules for latency and health
Audit loggingRecommendedOn by default (DAKERA_AUDIT_ENABLED); ship the audit lines to your log store
Health check probesRecommendedDocker HEALTHCHECK or K8s liveness/readiness probes
Scheduled backupsRecommendedDaily with 30-day retention via admin API
Set resource limitsRecommendedCPU/RAM limits per sizing guide above
dakera --check-config before every changeRecommendedLists what the next start would warn about or refuse (exit 0 or 78)
Set RUST_LOG=infoRecommendedAvoid debug in production — verbose and slow

Environment variable reference

The most-used variables, grouped by category. Every variable, with the values it accepts, is in the Configuration reference.

Core

VariableDefaultDescription
DAKERA_HOST0.0.0.0Server bind address
DAKERA_PORT3000REST API port
DAKERA_ROOT_API_KEY—Root authentication key (required)
DAKERA_GRPC_ENABLEDtrueEnable gRPC API
DAKERA_GRPC_PORT50051gRPC server port
DAKERA_REQUEST_TIMEOUT300Request timeout in seconds; also bounds every gRPC call
DAKERA_MAX_BODY_SIZE10485760Max request body size in bytes (10 MiB)
DAKERA_HTTP_HEADER_READ_TIMEOUT_SECS30Seconds a client has to send a request head; idle keep-alive and HTTP/2 connections close after it. Keep a proxy's upstream idle timeout below it (v0.12.3)
DAKERA_ALLOWED_HOSTSunsetWith authentication off: dotted host names the server answers (comma-separated, * = any); others get 421 (v0.12.3)
DAKERA_MAX_MEMORY_CONTENT_BYTES100000Longest memory content in UTF-8 bytes, on every memory write path (v0.12.2)
DAKERA_SESSION_IDLE_TIMEOUT_SECS14400 (4 h)Sessions idle this long are ended by the server; 0 = only sessions that set their own timeout are ended; at most 30 days (v0.12.2)
DAKERA_EVENT_BUS_CAPACITY256SSE broadcast channel capacity
RUST_LOGinfoLog level filter

Storage

VariableDefaultDescription
DAKERA_STORAGEmemoryBackend: memory, segmented, filesystem or s3; anything else refuses a fresh install
DAKERA_STORAGE_PATH./dataThe data root: every local path (write-ahead log, tiers, knowledge graph, filesystem storage) derives from it. DAKERA_DATA_DIR is an alias
DAKERA_S3_BUCKET—S3 bucket name (required for S3 backend)
DAKERA_S3_REGIONAWS_REGIONS3 signing region, required with DAKERA_STORAGE=s3 (MinIO accepts us-east-1)
DAKERA_S3_ENDPOINT—S3/MinIO endpoint URL
DAKERA_TIERED_STORAGEfalseEnable hot → warm → cold tiering
DAKERA_L1_CACHE_SIZE100000 vectorsHot-tier budget: a vector count or a byte size such as 512MB
DAKERA_HOT_TO_WARM_SECS3600Idle seconds before a record moves from hot to warm
DAKERA_WARM_TO_COLD_SECS86400Idle seconds before a record moves from warm to cold (S3)
DAKERA_CACHE_DIR{root}/cache/warmWarm tier local disk path
DAKERA_HOT_TIERrocksdbTiered hot tier: rocksdb (durable) or memory
DAKERA_ROCKSDB_SYNCtruefsync RocksDB hot-tier writes
DAKERA_S3_TIMEOUT_SECS / DAKERA_S3_IO_TIMEOUT_SECS30 / 60Per-attempt S3 deadlines for control requests / data calls

Security

VariableDefaultDescription
DAKERA_ENCRYPTION_KEY—AES-256-GCM key (64 hex chars or passphrase ≥8 chars)
DAKERA_CORS_ORIGINSunset (all origins; with authentication off, loopback origins only)Comma-separated allowed origins
DAKERA_CORS_CREDENTIALSfalseAllow credentials in CORS; ignored with a startup warning unless origins are named (never with a wildcard)
DAKERA_CSP_ENABLEDtrueEnable Content Security Policy
DAKERA_CSP_DIRECTIVESdefault-src 'none'; frame-ancestors 'none'CSP directive string
DAKERA_HSTS_ENABLEDtrueEnable HSTS header
DAKERA_HSTS_MAX_AGE31536000HSTS max-age (1 year)
DAKERA_FRAME_OPTIONSDENYX-Frame-Options value
DAKERA_RATE_LIMIT_RPS100Rate limit: requests per second (RATE_LIMIT_RPS is the deprecated pre-v0.12 name, still honored with a warning)
DAKERA_RATE_LIMIT_BURST50Rate limit: burst capacity (deprecated alias RATE_LIMIT_BURST)

Audit & tracing

VariableDefaultDescription
DAKERA_AUDIT_ENABLEDtrueEnable audit logging
DAKERA_AUDIT_REQUEST_BODYfalseLog request bodies
DAKERA_AUDIT_RESPONSE_BODYfalseLog response bodies
DAKERA_AUDIT_MAX_BODY_SIZE4096Max body size to log (bytes)
DAKERA_AUDIT_EXCLUDE_PATHS/health,/health/ready,/health/live,/metricsPath prefixes excluded from audit
DAKERA_TRACING_ENABLEDfalseEnable OpenTelemetry OTLP export
OTEL_EXPORTER_OTLP_ENDPOINThttp://localhost:4317OTLP exporter endpoint
OTEL_SERVICE_NAMEdakeraService name for traces

Clustering

VariableDefaultDescription
DAKERA_CLUSTER_MODEfalseEnable multi-node cluster
DAKERA_CLUSTER_NODE_IDgenerated onceStable node identifier (generated once and kept in {data root}/cluster-node-id when unset)
DAKERA_CLUSTER_SECRET—Shared secret, at least 16 characters, the same on every node; authenticates node-to-node routes and gossip. Required in cluster mode, fresh install or upgraded node (v0.12.1)
DAKERA_CLUSTER_SEED_NODES—Comma-separated host:port seed addresses; required in cluster mode (legacy alias DAKERA_CLUSTER_SEEDS)
DAKERA_GOSSIP_PORT7946SWIM gossip port
DAKERA_CLUSTER_BIND_ADDR0.0.0.0:7946Gossip bind address (legacy alias DAKERA_GOSSIP_BIND)
DAKERA_REDIS_URL—Redis URL for distributed cache

Inference & retrieval

VariableDefaultDescription
DAKERA_ML_CLASSIFIERfalseEnable the ML query classifier. Set explicitly, a classifier that fails to initialize refuses a fresh install
DAKERA_TEMPORAL_BOOST0 (off; 0 to 1)Temporal scoring boost factor
DAKERA_TEMPORAL_WINDOW_DAYS365 (30 to 3650)Temporal window for recency scoring
DAKERA_RRF_K60Reciprocal Rank Fusion k parameter
DAKERA_ANN_THRESHOLD1000Minimum vectors for an ANN index (below it the scan is exact)
DAKERA_HNSW_CACHE_MAX50HNSW index cache max entries

AutoPilot & decay

VariableDefaultDescription
DAKERA_AUTOPILOT_ENABLEDtrueEnable AutoPilot background tasks
DAKERA_AUTOPILOT_DEDUP_INTERVAL_HOURS6Deduplication cycle interval
DAKERA_AUTOPILOT_CONSOLIDATION_INTERVAL_HOURS12Consolidation cycle interval
DAKERA_AUTOPILOT_DEDUP_THRESHOLD0.93Cosine similarity threshold for dedup
DAKERA_DECAY_STRATEGYexponentialDecay strategy: exponential, linear, step
DAKERA_DECAY_HALF_LIFE_HOURS168Half-life for exponential decay
DAKERA_DECAY_INTERVAL_SECS3600Decay engine cycle interval
DAKERA_DECAY_MIN_IMPORTANCE0.01Importance floor: decay lowers importance to this value and does not delete the memory

Related: Dakera Dashboard (admin UI) · Security hardening · High availability & clustering · Architecture · Full configuration reference · Self-hosted deployment walkthrough

Want zero-ops instead?

Dakera Cloud is coming — managed hosting, automatic upgrades, and built-in HA. No Docker to manage, no backup scripts, no ops overhead. Reserve your spot for founding pricing.

Join Cloud Waitlist → High Availability Guide →
Stay sharp on agent memory
Benchmark results, SDK releases, and production patterns. Under 500 words per issue.
✓ You're in. First issue lands soon — watch for Dakera in your inbox.