Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ Every stack starts from the same [standard setup](documentation/standard-setup.m
| **Gotify** | Push notification server that shows messages from your scripts in its web interface and Android app. | [Details](services/gotify) |
| **Hemmelig** | Archived. Encrypted secret sharing by link, with secrets deleted after reading or on expiry. | [Details](services/hemmelig) |
| **Homebox** | Home inventory for items, with location, quantity, warranty, and purchase details. | [Details](services/homebox) |
| **Kasm** | Platform that streams desktops and apps to your browser, each in a container. | [Details](services/kasm) |
| **LubeLogger** | Tracker for vehicle maintenance and fuel use, with reminders for upcoming services. | [Details](services/lube-logger) |
| **Mini QR** | Web app to create, style, and scan QR codes. | [Details](services/miniqr) |
| **ntfy** | Notification service that lets scripts and apps push messages to phones and browsers over HTTP. | [Details](services/ntfy) |
Expand Down
28 changes: 28 additions & 0 deletions services/kasm/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#URL=https://github.com/tailscale-dev/ScaleTail
# To run a second copy, copy the folder under another name and change SERVICE.

# Service Configuration
# Service name (e.g., adguard). Used as hostname in Tailscale and for container naming.
SERVICE=kasm
# Docker image URL from container registry (e.g., adguard/adguardhome:latest).
# Kasm publishes no "latest" tag, so pin the release.
IMAGE_URL=kasmweb/workspaces:1.19.0

# Network Configuration
# Port to expose to local network. Uncomment the "ports:" section in compose.yaml to enable. Kasm web port; keep it equal to KASM_PORT in compose.yaml.
SERVICEPORT=4443
# Preferred DNS server for Tailscale. Uncomment the "dns:" section in compose.yaml to enable.
DNS_SERVER=9.9.9.9

# Tailscale Configuration
# Auth key from https://console.tailscale.com/admin/settings/keys. See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions.
TS_AUTHKEY=

# Time Zone setting for containers. See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
TZ=Europe/Amsterdam

# Any Container environment variables are declared below. See https://docs.docker.com/compose/how-tos/environment-variables/

# Optional: Docker Hub login, to avoid pull rate limits when Kasm downloads its images.
DOCKER_HUB_USERNAME=
DOCKER_HUB_PASSWORD=
53 changes: 53 additions & 0 deletions services/kasm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Kasm Workspaces

[Kasm Workspaces](https://www.kasm.com/community-edition) streams desktops, browsers, and applications to your web browser, each in its own container. This stack runs the Community Edition from the all-in-one image that Kasm publishes.

This stack runs Kasm Workspaces with a Tailscale sidecar, as described in [the standard setup](../../documentation/standard-setup.md). The image does not contain Tailscale, so the sidecar is what puts Kasm on your Tailnet.

## At a glance

| Item | Value |
| -------------- | -------------------------------------------------------------------- |
| Web interface | `https://kasm.<tailnet>.ts.net` |
| Service port | `4443` (HTTPS with a self-signed certificate) |
| Install wizard | `https://<Tailscale IP>:3000` (HTTPS with a self-signed certificate) |
| Image | `kasmweb/workspaces:1.19.0` |
| Data | `./kasm-data/opt/` |

## Before you start

- Reserve disk space. Kasm runs its own Docker, and everything it downloads goes to `./kasm-data/opt/`. A test install with one workspace image (Alpine desktop) used about 13 GB.
- Reach the install wizard on port `3000` of the device's Tailscale IP address. Your Tailnet policy must allow that port. Find the address with `docker exec tailscale-kasm tailscale ip -4`.
- Optional: put a Docker Hub username and password or access token in `.env`, so that Kasm is not limited by the anonymous pull rate when it downloads its images.

## Deviations from the standard setup

- **Privileged container.** Kasm runs its own Docker daemon inside the container (Docker-in-Docker) to start the services and the workspaces, so the `application` service has `privileged: true`.
- **Port `4443`, not `443`.** Tailscale Serve listens on port `443` of the Tailscale IP address, and Kasm cannot also publish `443` in the shared network namespace: the install fails with `failed to bind host port 0.0.0.0:443/tcp: address already in use`. `KASM_PORT` is `4443`, and Serve forwards to it. The install wizard passes `KASM_PORT` to the Kasm installer and upgrader, so a new value takes effect only with a new install or an upgrade.
- **Serve forwards to HTTPS.** Kasm serves its web interface on `4443` with a self-signed certificate. Tailscale Serve forwards to it with `https+insecure`.
- **Extra ports.** Port `4443` (Kasm directly, with the self-signed certificate) and port `3000` (install wizard) are reachable on the Tailscale IP address of the device, when your Tailnet policy allows it. Uncomment the `ports` lines to publish them on your local network.
- **Health check.** The check calls the Kasm API on port `4443`, which does not listen until you finish the install wizard. The container is `starting` for 10 minutes, and `unhealthy` after that until the install is complete.
- **No `/config` volume.** Kasm keeps all of its data under `/opt`, which is `./kasm-data/opt/`.

## First run

1. Open `https://<Tailscale IP>:3000` and accept the self-signed certificate warning.
2. Accept the license, then set the passwords of `admin@kasm.local` and `user@kasm.local`. A password has at least 8 characters, with an uppercase letter, a lowercase letter, a number, and one of `!#$%^&*@`.
3. Pick the workspace images to download, such as Firefox or an Alpine desktop, and start the install. Kasm downloads its own images first, so this takes several minutes.
4. When the wizard says the install is complete, open `https://kasm.<tailnet>.ts.net` and sign in. Start a workspace to check that it streams.

The wizard stays available on port `3000` after the install, and it has no sign-in. When you no longer need it, use "Stop Install Wizard" on its dashboard. It disables the wizard until you remove `./kasm-data/opt/NO_WIZARD` and restart the container, which an upgrade needs.

The "Web URL" link on the wizard dashboard points to port `4443`. Use the Tailscale Serve address instead, which has a valid certificate.

## Troubleshooting

- **The install fails with `address already in use`.** Something else uses the Kasm port in the shared network namespace, for example when `KASM_PORT` is `443`. Use a different port in `compose.yaml` (`KASM_PORT` and the Serve configuration), stop the stack, delete `./kasm-data/opt/`, and run the install wizard again.
- **The wizard says that Docker is still loading.** Refresh the page after a minute.

## Links

- [Kasm Workspaces documentation](https://docs.kasm.com/)
- [Kasm Workspaces Community Edition](https://www.kasm.com/community-edition)
- [kasm-workspaces-dind: the all-in-one image](https://github.com/kasmtech/kasm-workspaces-dind)
- [kasmweb/workspaces on Docker Hub](https://hub.docker.com/r/kasmweb/workspaces)
73 changes: 73 additions & 0 deletions services/kasm/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
configs:
ts-serve:
content: |
{"TCP":{"443":{"HTTPS":true}},
"Web":{"$${TS_CERT_DOMAIN}:443":
{"Handlers":{"/":
{"Proxy":"https+insecure://127.0.0.1:4443"}}}},
"AllowFunnel":{"$${TS_CERT_DOMAIN}:443":false}}

services:
# Make sure you have updated/checked the .env file with the correct variables.
# Every variable used in this file must be defined there, unless it has a default such as ${VAR:-value}.
# Tailscale Sidecar Configuration
tailscale:
image: tailscale/tailscale:latest # Image to be used
container_name: tailscale-${SERVICE} # Name for local container management
hostname: ${SERVICE} # Name used within your Tailscale environment
environment:
- TS_AUTHKEY=${TS_AUTHKEY}
- TS_STATE_DIR=/var/lib/tailscale
- TS_SERVE_CONFIG=/config/serve.json # Tailscale Serve configuration to expose the web interface on your local Tailnet - remove this line if not required
- TS_USERSPACE=false
- TS_ENABLE_HEALTH_CHECK=true # Enable healthcheck endpoint: "/healthz"
- TS_LOCAL_ADDR_PORT=127.0.0.1:41234 # The <addr>:<port> for the healthz endpoint
#- TS_ACCEPT_DNS=true # Uncomment only if the service must resolve MagicDNS names - this replaces Docker DNS, so Compose service names no longer resolve
- TS_AUTH_ONCE=true
configs:
- source: ts-serve
target: /config/serve.json
volumes:
- ./config:/config # Config folder used to store Tailscale files - you may need to change the path
- ./ts/state:/var/lib/tailscale # Tailscale requirement - you may need to change the path
devices:
- /dev/net/tun:/dev/net/tun # Network configuration for Tailscale to work
cap_add:
- net_admin # Tailscale requirement
#ports:
# - 0.0.0.0:${SERVICEPORT}:${SERVICEPORT} # Binding the Kasm web port to the local network - may be removed if only exposure to your Tailnet is required
# - 0.0.0.0:3000:3000 # Binding the install wizard port to the local network - add it only while you install or upgrade
# If any DNS issues arise, use your preferred DNS provider by uncommenting the config below
#dns:
# - ${DNS_SERVER}
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"] # Check Tailscale has a Tailnet IP and is operational
interval: 1m # How often to perform the check
timeout: 10s # Time to wait for the check to succeed
retries: 3 # Number of retries before marking as unhealthy
start_period: 10s # Time to wait before starting health checks
restart: always

# Application
application:
image: ${IMAGE_URL} # Image to be used
privileged: true # Kasm runs its own Docker daemon inside the container (Docker-in-Docker), which needs a privileged container
network_mode: service:tailscale # Sidecar configuration to route the service through Tailscale
container_name: app-${SERVICE} # Name for local container management
environment: # Variables are declared in .env file.
- TZ=${TZ}
- KASM_PORT=4443 # Kasm web port in the shared network namespace. Not 443, which Tailscale Serve uses. Keep it equal to the Serve port. The wizard applies it only when it installs or upgrades Kasm
- DOCKER_HUB_USERNAME=${DOCKER_HUB_USERNAME} # Optional: Docker Hub login, to avoid pull rate limits
- DOCKER_HUB_PASSWORD=${DOCKER_HUB_PASSWORD} # Optional: Docker Hub password or access token
volumes:
- ./${SERVICE}-data/opt:/opt # Docker and Kasm storage
depends_on:
tailscale:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-fsk", "https://127.0.0.1:4443/api/__healthcheck"] # Check if the Kasm API is responding (self-signed certificate, so -k)
interval: 1m # How often to perform the check
timeout: 10s # Time to wait for the check to succeed
retries: 3 # Number of retries before marking as unhealthy
start_period: 10m # Kasm does not listen on its web port until you finish the install wizard
restart: always
Loading