No description
Find a file
Niklas Vorberg 7c93cfe15f Fix HA compose after gating kasm_agent_2 behind dual-agent profile.
Remove proxy_2 dependency on the optional second agent so the HA stack starts without --profile dual-agent.
2026-06-16 21:39:23 +02:00
scripts Add repair-agents command and keep second agent off by default. 2026-06-16 21:36:15 +02:00
.env.example Add official Kasm 1.17.0 Docker stack with HA and NPM support. 2026-06-16 12:37:51 +02:00
.gitignore Add official Kasm 1.17.0 Docker stack with HA and NPM support. 2026-06-16 12:37:51 +02:00
docker-compose.ha.local.yml Add official Kasm 1.17.0 Docker stack with HA and NPM support. 2026-06-16 12:37:51 +02:00
docker-compose.ha.yml Fix HA compose after gating kasm_agent_2 behind dual-agent profile. 2026-06-16 21:39:23 +02:00
docker-compose.local.yml Add official Kasm 1.17.0 Docker stack with HA and NPM support. 2026-06-16 12:37:51 +02:00
docker-compose.yml Add repair-agents command and keep second agent off by default. 2026-06-16 21:36:15 +02:00
README.md Add repair-agents command and keep second agent off by default. 2026-06-16 21:36:15 +02:00

Kasm Workspaces 1.17.0 — Docker Compose Stack

Production-ready Kasm Workspaces stack based on the official 1.17.0 service images and installer configuration layout.

Architecture

Layer Services Network
Data kasm_db, kasm_redis kasm-internal only
App kasm_api, kasm_manager, kasm_share, guac/rdp kasm-internal only
Sessions kasm_agent (+ optional kasm_agent_2 on separate hosts only) kasm-internal + kasm_default_network
Entry kasm_proxy kasm-internal + proxy-network

HA extension (docker-compose.ha.yml): adds a second API/manager/proxy tier for NPM load balancing.

Requirements

  • Linux host with Docker Engine (recommended for agents/session containers)
  • Docker Compose v2
  • External networks: kasm-internal, kasm_default_network, proxy-network
  • Node Proxy Manager on proxy-network (production)
  • Sufficient CPU/RAM for concurrent sessions (one agent on single-node; scale vertically or add remote agent hosts)

Quick start (Node Proxy Manager)

cp .env.example .env
# Edit passwords and KASM_PUBLIC_HOSTNAME

docker network create kasm-internal 2>/dev/null || true
docker network create kasm_default_network 2>/dev/null || true
# proxy-network should already exist from your NPM stack

./scripts/bootstrap.sh prepare
docker compose up -d db kasm_redis
./scripts/bootstrap.sh init-db
docker compose up -d

NPM configuration

Create a Proxy Host:

Setting Value
Domain your Kasm domain
Forward Hostname kasm_proxy
Forward Port 443
Scheme https
Websockets On
Block common exploits On

Recommended custom locations / advanced:

  • Increase proxy read/send timeout (e.g. 3600s) for streaming sessions
  • Ensure the NPM container is attached to proxy-network

HA with NPM

docker compose -f docker-compose.yml -f docker-compose.ha.yml up -d

Add both upstreams to NPM (custom nginx config or multiple hosts with same domain):

  • kasm_proxy:443
  • kasm_proxy_2:443

Local access (host ports)

docker compose -f docker-compose.yml -f docker-compose.local.yml up -d
# UI: https://localhost:8443

With HA locally:

docker compose -f docker-compose.yml -f docker-compose.ha.yml -f docker-compose.local.yml -f docker-compose.ha.local.yml up -d
# Primary: https://localhost:8443
# Secondary: https://localhost:8444

Default logins

Set in .env (ADMIN_PASSWORD, USER_PASSWORD). Defaults from bootstrap output:

  • admin@kasm.local
  • user@kasm.local

Files

File Purpose
docker-compose.yml Core stack (NPM-ready)
docker-compose.ha.yml Second app/proxy tier
docker-compose.local.yml Host port overrides
docker-compose.ha.local.yml HA second proxy port override
scripts/bootstrap.sh Downloads release, certs, config, DB seed
.env Secrets and settings

Runtime data is generated under ./data/kasm/1.17.0/ (gitignored).

Bootstrap commands

./scripts/bootstrap.sh prepare          # Download release + generate runtime config
./scripts/bootstrap.sh init-db          # Seed DB (requires db + redis running)
./scripts/bootstrap.sh init-db --fresh  # Wipe DB volume + seed (use after rm -rf data/)
./scripts/bootstrap.sh patch-secrets    # Re-sync DB/Redis passwords into api config
./scripts/bootstrap.sh all              # prepare + db + full stack

If you use compose override files (HA or local ports), export COMPOSE_FILE before bootstrap so init-db stops the same services you started:

export COMPOSE_FILE=docker-compose.yml:docker-compose.local.yml
# or: docker-compose.yml:docker-compose.ha.yml

Scaling concurrent users

  • Agents: one agent per central proxy upstream; add remote agent hosts with their own proxy/nginx path, or scale the host vertically
  • App tier: use docker-compose.ha.yml or add more api/manager/proxy replicas
  • Resources: size agents per Kasm sizing guide

Troubleshooting

docker compose ps
docker compose logs -f proxy
docker compose logs -f kasm_agent

Agents not registering: verify MANAGER_TOKEN in .env matches seeded value; restart agents.

NPM 502: confirm kasm_proxy is on proxy-network and healthy on port 443.

relation "settings" does not exist / 502 from proxy: database was not seeded. Stop the stack, run ./scripts/bootstrap.sh init-db --fresh, wait for "Database seed verified", then docker compose up -d.

init-db stuck on "pg_isready but not psql": Kasm Postgres requires SSL for remote connections (hostssl in pg_hba.conf). Ensure you have the latest bootstrap.sh (uses PGSSLMODE=require for psql). If the db container shows exited, run docker compose up -d db and retry.

password authentication failed for user "kasmapp": the DB volume was initialized with a different POSTGRES_PASSWORD than your current .env (common after changing .env without wiping the volume). Either run ./scripts/bootstrap.sh init-db --fresh (latest bootstrap removes the db container before deleting the volume), or sync manually:

docker compose exec -T db psql -U kasmapp -d postgres -c "ALTER USER kasmapp WITH PASSWORD 'YOUR_POSTGRES_PASSWORD';"
./scripts/bootstrap.sh init-db

Do not run docker compose up -d (full stack) until init-db completes successfully.

kasm_share Redis invalid password: kasm_share reads redis.redis_password from data/kasm/1.17.0/conf/app/api/api.app.config.yaml. It must match the password the kasm_redis container was started with (REDIS_PASSWORD in .env). Common causes: an older bootstrap wrote the DB password into redis_password, .env was edited after Redis started, or CRLF/trailing spaces in .env (Compose vs bash disagree on the value).

Fix without re-seeding:

./scripts/bootstrap.sh patch-secrets
docker compose restart kasm_share kasm_api kasm_manager
docker compose logs kasm_share --tail 20

Diagnose:

set -a && source .env && set +a
grep -A3 '^redis:' data/kasm/1.17.0/conf/app/api/api.app.config.yaml
docker compose exec kasm_redis redis-cli -a "$REDIS_PASSWORD" ping

Kasm recommends Redis passwords without $, quotes, or other special characters. If you use $ in .env, quote the value (REDIS_PASSWORD='your$password') so Compose and bash expand it the same way.

Seed fails with Permission denied: /tmp/.temp.api.app.config.yaml: tmp/api must be writable by KASM_UID (default 1000). After updating bootstrap, run ./scripts/bootstrap.sh prepare again or:

chown -R 1000:1000 data/kasm/1.17.0/tmp/api
./scripts/bootstrap.sh init-db --fresh

kasm_rdp_gateway crash loop / Permission denied on /tmp/redemption-sesman-sock or rdpproxy/var/pid: RDP services run as KASM_UID (default 1000) with a read-only root filesystem. Bootstrap must own tmp/rdpgw, RDP config dirs, and the nginx TLS key for the HTTPS gateway. After updating bootstrap:

./scripts/bootstrap.sh prepare   # re-runs permission fix at end of patch_configs
docker compose restart rdp_gateway kasm_rdp_https_gateway

Or fix ownership manually without re-preparing:

chown -R 1000:1000 data/kasm/1.17.0/tmp/rdpgw \
  data/kasm/1.17.0/conf/app/rdp_gateway \
  data/kasm/1.17.0/conf/app/rdp_https_gateway \
  data/kasm/1.17.0/certs/kasm_nginx.key \
  data/kasm/1.17.0/certs/kasm_nginx.crt
chmod 755 data/kasm/1.17.0/tmp/rdpgw/var
docker compose restart rdp_gateway kasm_rdp_https_gateway

Early 502 responses and ENOTFOUND proxy in logs during the first ~30s after docker compose up are normal startup race; components retry until kasm_api and kasm_manager are listening.

"No resource available" / "No Agent slots available" after docker compose restart or up -d: A full stack restart can re-enable a second agent, reset hostnames, or leave agents at 0 cores. Run:

./scripts/bootstrap.sh repair-agents

This re-applies the single-agent upstream, hostname: proxy, manager token sync, disables extra agents in the DB, sets cores_override (default 8, override with KASM_CORES_OVERRIDE in .env), stops kasm_agent_2, and restarts proxy/api/manager/agent. Wait ~30s, then try a session again.

Sessions fail with 401 Unauthorized on agent hello / "Agent did not respond to hello": With a single central proxy, only one agent may be registered with hostname: proxy and listed in upstream_agent.conf. Nginx round-robins /agent/ across upstream members; hello requests are signed per server_id, so a second agent on the same upstream always causes intermittent 401s. Fix on existing installs:

cat > data/kasm/1.17.0/conf/nginx/upstream_agent.conf <<'EOF'
upstream kasm_agent {
  server kasm_agent:4444;
}
EOF

AGENT1_ID=$(grep server_id data/kasm/1.17.0/agents/agent-1/agent.app.config.yaml | awk '{print $2}')
docker compose exec -T db psql -U kasmapp -d kasm -c \
  "UPDATE servers SET enabled = false WHERE server_type = 'host' AND server_id != '${AGENT1_ID}';"

docker compose stop kasm_agent_2
docker compose restart proxy kasm_api kasm_manager kasm_agent

If hello still returns 401 after the single-agent fix, the manager token in agent configs likely drifted from the database (re-running prepare without init-db). Do not UPDATE settings.token via SQL — Kasm stores it encrypted; writing plain text causes ValueError: block length errors. Instead, read the plain token in Admin → Settings → Manager → Token, set the same value in .env (MANAGER_TOKEN=) and both agent configs (manager.token), then restart kasm_agent and kasm_manager. Or re-seed: ./scripts/bootstrap.sh prepare && ./scripts/bootstrap.sh init-db.

Scale sessions by sizing the host or adding agents on separate hosts (each with its own proxy path), not by load-balancing multiple agent containers on one /agent/ upstream.

Sessions fail to start / "No Agent slots available" / connection refused to kasm_agent:443: In a single-proxy compose stack, agent records in the database must use hostname: proxy, not kasm_agent. The API reaches agents through central nginx at https://proxy:443/agent/.... Fix existing installs without re-seed:

docker compose exec -T db psql -U kasmapp -d kasm -c "UPDATE servers SET hostname = 'proxy' WHERE server_type = 'host';"
docker compose restart kasm_agent kasm_agent_2 kasm_api

Verify from the API container:

docker compose exec kasm_api curl -sk https://proxy:443/agent/api/v1/hello/

If you expose Kasm on a non-443 port (e.g. 8443), set Admin → Zones → default → Proxy Port to that client-facing port.

Sessions fail to start / stuck on "Creating a secure connection": workspace containers are placed on kasm_default_network. Bootstrap creates it automatically; agents, guac, and proxy must be attached (see docker-compose.yml). After updating compose, recreate affected services:

docker network create kasm_default_network 2>/dev/null || true
docker compose up -d kasm_agent kasm_agent_2 kasm_guac proxy rdp_gateway kasm_rdp_https_gateway

Re-running prepare without init-db: regenerates agent/API/manager UUIDs and registration tokens in config files while the database still holds old values. Agents fail to register or sessions behave oddly. Always run init-db after prepare, or use patch-secrets only for password changes.

ARM64 / Apple Silicon: RDP gateway images are amd64-only; bootstrap skips RDP nginx wiring on arm64. Disable or ignore rdp_gateway / kasm_rdp_https_gateway on arm64 hosts.

Password characters: Kasm docs recommend avoiding $, ', and " in Postgres/Redis passwords. Quote .env values that contain $. Use ./scripts/bootstrap.sh patch-secrets after any password change.

Redis host tuning (optional): on Linux production hosts, consider sysctl vm.overcommit_memory=1 and disabling transparent huge pages for Redis stability (warnings in kasm_redis logs).

Notes

  • First bootstrap downloads Kasm 1.17.0 release tarball (~cache/kasm_release)
  • Self-signed certs are generated for internal/proxy TLS; NPM typically terminates public TLS
  • Session containers require Linux + /var/run/docker.sock on agent hosts