Operations¶
Runbooks for the people who keep a SlotServe installation running: upgrading, backups and restore, the first admin, reverse proxies, health checks and alerts, container hardening, firewall ports and checking an installation on real hardware. Each problem runbook reads symptom → diagnose → fix → prevent.
The commands assume the Docker Compose installation from Getting started, run in the folder
with docker-compose.yml. Without Docker, drop the docker compose exec slotserve prefix and run slotserve ...
as the user that runs SlotServe, with the same environment (.env).
| Situation | Go to |
|---|---|
| Moving from v0.8 to v0.10 | Upgrade from v0.8 |
| An upgrade went wrong | Upgrade failed: roll back |
| Setting up or checking backups | Backups, The secret key |
| Putting a backup back, moving to another machine | Restore from a backup, Move to a new machine |
| Nobody can sign in as admin | First admin and setup code, Forgotten admin password |
| Running behind nginx, Caddy or Traefik | Reverse proxy |
| Something is red on Admin → Overview | Health checks and slotserve doctor, Inbox and alerts |
sudo or an app does not work in an environment |
Container hardening and compatibility mode |
| Which ports to open | Ports and firewall |
| Disk full, GPUs gone, environments do not start, a leaked password | Runbooks |
| Checking an installation before people use it | Verify on real hardware |
Upgrade from v0.8¶
v0.10 changes how people sign in, how dev environments are isolated and how data is backed up. Most of it needs
nothing from you; this section lists what people will notice and what to check. The full list is under Breaking
changes in CHANGELOG.md.
Before you upgrade¶
- Read the Breaking changes of the release.
- Make a backup you can find again: Admin → Overview → Backups → Back up now, or stop SlotServe and copy the
whole data directory (
DATA_DIR, default./data). The upgrade also takes its own snapshot (below). - Note your image tag.
docker-compose.ymlnow follows a minor version (ghcr.io/peleryuan/slotserve:0.10, which gets 0.10.x fixes) instead oflatest. If your.envsetsSLOTSERVE_IMAGE, change it to the new version. - Tell people about the one-time sign-in and the SSH host key warning below. Running dev environments keep running while SlotServe restarts; the new container settings apply on their next start.
Upgrade¶
git pull # the new docker-compose.yml and docs
docker compose pull # or: docker compose up -d --build
docker compose up -d
docker compose logs -f slotserve
On the first start the server:
- saves a snapshot before it changes the database:
data/backups/slotserve-v<old>-to-v<new>-<time>/with the database,secret_keyand the dev environment SSH host keys. Snapshots are never deleted automatically; - applies the numbered database migrations all or nothing (a failure rolls back and the server does not start;
see Roll back) and writes
data/migration_report.json; - generates a secret key in
data/secret_keyifSLOTSERVE_SECRET_KEYis unset or stillchange-me(see The secret key).
reset-password and set-admin refuse to run on a database that still needs upgrading: start the server once
first.
What people notice¶
Everyone signs in once more. Browser sign-ins are now stored on the server, so all old sign-ins end with the upgrade. API keys keep working unchanged. Changing your password now signs out your other browsers.
SSH warns about a changed host key, once per environment. Every dev environment now has its own SSH host key
(before, all environments shared one that anyone could read inside their container). Existing environments get
their key on their next start, and the first ssh afterwards stops with REMOTE HOST IDENTIFICATION HAS CHANGED.
That is expected exactly once. Tell people to:
- compare the fingerprint
sshprints with the Host key shown on the environment's card (Dev environments); -
remove the old entry and connect again:
A warning after that first time, for an environment whose key has not changed on its card, is a real warning: do not ignore it. Kept environments created by an older version are snapshotted and recreated once on their next start (that start takes longer), because the new settings cannot be added to an existing container.
Shared environments of idle lanes need Join. They accept SSH keys only, and only people who joined them get an account (Dev environments → Join). Everyone who had an SSH key at the upgrade was joined to every lane.
Containers are hardened. sudo and other setuid programs stop working inside dev environments, ping does
not work, and a fork bomb or out-of-memory job stays inside its container. A memory limit of 0 now means "the
host's memory less max(4 GB, 10 %)" instead of "no limit". A lane that names no GPUs cannot start a GPU
environment any more: give the lane its GPUs in Admin → GPUs & lanes. Templates that really need sudo or
their own init system run in compatibility mode; see Container hardening.
Sign-ups. Installations that already have an admin are not asked for a setup code. While the invite code is
still change-me, nobody can sign up: set an invite code or hand out invite links (Admin → Users).
Behind a reverse proxy, changes in the web UI may be refused with "did not come from a SlotServe page" until
the proxy passes the Host header or SLOTSERVE_PUBLIC_URL is set, and sign-in throttling needs
SLOTSERVE_FORWARDED_ALLOW_IPS. See Reverse proxy.
Firewall. Dev environment web interfaces (VS Code, JupyterLab, the desktop) are served on their own port, 8001 by default. Open it if people use them; see Ports and firewall.
Check the upgrade¶
- Admin → Overview: the setup checklist and the system health table. Every check should be OK or a note you understand; see Health checks.
docker compose exec slotserve slotserve doctorgives the same list on the command line.data/migration_report.jsonlists every migration step; the health check migration warns when an upgrade could not keep some rows.- Sign in, open a dev environment in the browser, call a model in the Playground.
Upgrade failed: roll back¶
Symptom. The server does not come up after an upgrade, or it runs but something you rely on is broken.
Diagnose.
docker compose logs slotserveshows why. A failed migration saysdatabase upgrade failed and was rolled back; the snapshot is in ...: the database is unchanged and still the old version's.- An older version started on an upgraded database refuses to start with
... was used by a newer SlotServe. This protects your data; it is not the problem itself.
Fix. To run the previous version again:
docker compose stop slotserve
ls -d data/backups/slotserve-v*-to-v* # the newest is the snapshot of this upgrade
SNAP=data/backups/slotserve-v...-to-v...-<time> # the folder name from ls
cp "$SNAP/slotserve.db" data/slotserve.db
rm -f data/slotserve.db-wal data/slotserve.db-shm
[ -f "$SNAP/secret_key" ] && cp "$SNAP/secret_key" data/secret_key
[ -d "$SNAP/devenv" ] && cp -r "$SNAP/devenv" data/
Then pin the old version in .env and start it:
Everything that happened between the upgrade and the rollback (bookings, keys, settings) is lost with the newer
database; keep data/slotserve.db somewhere before overwriting it if you may need it. When the migration itself
failed (the database was not changed), only pinning the old version is needed.
Prevent. Rehearse the upgrade on a copy of the data directory first (another folder as DATA_DIR, another
PORT, dev environments off). Keep the image pinned to a minor version and upgrade on purpose. Report a failed
migration with data/migration_report.json and the log.
Backups¶
What a backup contains¶
Every day at backup_hour (default 3:00 in the SlotServe time zone; -1 = every 24 hours from the last one)
SlotServe writes a bundle, a folder data/backups/slotserve-daily-<date>-<time>/ with:
| File | What it is |
|---|---|
slotserve.db |
A consistent copy of the database (SQLite's online backup, safe while the server runs) |
secret_key |
The generated secret key; absent when the key comes from SLOTSERVE_SECRET_KEY |
devenv/ |
The dev environments' SSH host keys |
manifest.json |
Version, schema, the key's fingerprint (never the key), sizes, checksums, row counts, the storages and kept environments, and what the bundle does not contain |
Admin → Overview → Backups lists the bundles, makes one on demand (Back up now, a manual bundle) and
downloads the database file. The newest backup_keep (default 7) daily and manual bundles are kept; upgrade
snapshots and prerestore bundles are never removed automatically. backup_keep 0 switches automatic backups off.
Not in a backup: /workspace and other kept directories, kept environments' containers, saved environment
images, models, and the data on managed storages. Back those up separately where they live:
- Storages (Admin → Storage: host folders, NFS, SMB): with the backup of that server or folder.
-
Docker volumes (when no storage and no storage directory is set), e.g. one person's
/workspace: -
Models: usually downloaded again, or copied with the models folder.
- Saved images:
docker savethe images you care about.
Keep a copy off the machine¶
A backup on the same disk does not survive the disk. Set Copy backups to (backup_target, Admin → System
settings) and every daily bundle is copied there after it is written, into a folder slotserve-backups/, keeping
the newest backup_keep copies:
-
An absolute path SlotServe can write, typically a mounted NAS share. With Docker this is a path inside the container: mount the share into it first, e.g. in
docker-compose.ymlundervolumes:and set
backup_targetto/backup-target. -storage:<id>: a storage from Admin → Storage (its id is in the list). SlotServe writes the bundle through a short-lived helper container, so the share does not need to be mounted into SlotServe.
The setup checklist item Backups kept elsewhere ticks once a bundle has been copied.
When a backup fails¶
The health checks backup and backup_age watch the backups, and admins are alerted (inbox, then chat platforms) when:
- the last backup failed (the evidence has the error; Try again makes a new one);
- the copy to the backup target failed or the target is not valid;
- the newest backup is older than 36 hours (the server was down, or the backup task is stuck).
A warning "Backups are only on this machine's disk" stays until a backup target is set (it can be dismissed with a
reason). /metrics exports slotserve_backup_age_seconds for external monitoring.
The secret key¶
The secret key signs browser sessions and encrypts the secrets SlotServe stores: storage passwords (SMB) and the viewable copies of API keys (when Let people view their API keys again is on). It does not protect the API keys themselves (they are stored as hashes) or passwords (also hashes).
- When
SLOTSERVE_SECRET_KEYis unset, empty orchange-me, SlotServe generates a random key on the first start and keeps it indata/secret_key(readable by its owner only). Every backup bundle includes it. - When
SLOTSERVE_SECRET_KEYis set to anything else, that value is used and is not written into backups: keep it with your other secrets (a password manager), next to the.env.
What to back up: the backup bundles (copied off the machine), plus .env and, if you set it, the secret key.
Copying the whole data directory while SlotServe is stopped is also a complete backup of SlotServe's own state.
Secret key lost or changed¶
Symptom. After a restart or a move everyone is signed out; the health check secrets says "N stored secret(s) cannot be decrypted with the current secret key"; SMB storages fail to mount; viewable API key copies cannot be shown.
Diagnose. The check's evidence lists what cannot be decrypted. slotserve doctor shows the same. Compare the
key's fingerprint in a bundle's manifest.json (secret_key.fingerprint) between a bundle from before and one
from after the change.
Fix.
- Put the old key back if you have it: the
secret_keyfile of a bundle from before the change (stop SlotServe, copy it todata/secret_key, start), or the old value ofSLOTSERVE_SECRET_KEYin.env. An explicitly setSLOTSERVE_SECRET_KEYalways wins overdata/secret_key: remove it from.envif you want the file to be used. - Without the old key, enter the listed secrets again: the password of each SMB storage in Admin → Storage; viewable API key copies cannot be recovered (the keys themselves keep working; their owners can make new ones). People sign in again.
Rotating the key on purpose (it leaked): stop SlotServe, set a new SLOTSERVE_SECRET_KEY (openssl rand -hex
32) or delete data/secret_key to have a new one generated, start, then re-enter the secrets as above. Everyone is
signed out.
Prevent. Keep backups off the machine; if you set the key yourself, store it outside the server.
Restore from a backup¶
slotserve restore puts a bundle back. The server must be stopped.
docker compose stop slotserve
docker compose run --rm --no-deps slotserve slotserve restore slotserve-daily-20261020-030000
docker compose up -d
- The argument is a bundle folder name in
data/backups/or a path the command can see (inside the container: put a bundle from the NAS intoDATA_DIR/backups/first). - It asks you to type
restore(--yesskips the question). It refuses a backup from a newer SlotServe and one that fails SQLite's integrity check. - Before it replaces anything, the current state is saved as a
slotserve-prerestore-<time>bundle, so a restore can itself be undone the same way. - It puts back the database, the secret key (
--keep-keykeeps the current one; secrets of the backup then cannot be decrypted) and the SSH host keys. A backup from an older version is upgraded on the next start, after its own snapshot. The command prints warnings you must act on, for example thatSLOTSERVE_SECRET_KEYin.envoverrides the restored key.
Older single-file backups (slotserve-daily-<time>.db) and upgrade snapshots can be restored the same way.
The reconciliation report¶
The restored database describes the moment of the backup, while Docker and the disk have moved on. On the next
start SlotServe compares them and shows the result as the health check restore on Admin → Overview (also
in data/restore_report.json):
| Finding | Meaning | What to do |
|---|---|---|
| Environment missing | The database expects a container that Docker does not have (removed after the backup, or a new machine) | For a kept environment, its installed software is gone; its kept directories are not affected. End or delete it, and start it again from its template. |
| Unknown container | A SlotServe container the database does not know (created after the backup) | Save what matters from it (docker cp, or let its owner copy files out), then remove it with docker rm -f <name>. |
| Model file missing | A model whose file is not where the database says | Copy the file back, or scan the models folder and remove the stale entry. |
| Storages | Listed for a re-check | Press Check for each in Admin → Storage. |
Volumes and ports are not compared. When Docker is not reachable the check waits ("waiting for Docker to compare") and runs once Docker is back.
Until an admin dismisses the restore check, SlotServe removes nothing the database does not know (no cleanup of unknown containers, networks or volumes). Read the report, deal with each line, then Dismiss it with a reason; normal cleanup resumes.
Move to a new machine¶
- Install SlotServe on the new machine as in Getting started, with the same or a newer
version, and copy
.env(it holdsSLOTSERVE_SECRET_KEYif you set one). Do not start it yet. -
Copy the newest bundle into the new
DATA_DIR/backups/and restore it: -
Copy what is not in the bundle: the models folder, Docker volumes or the storage directory with
/workspaceand kept directories, saved images (docker save/docker load). Storages on a NAS only need the new machine to reach them (NFS exports allow its address;nfs-common/cifs-utilsinstalled). - Start (
docker compose up -d) and read the reconciliation report: environments of the old machine are listed as missing. - Point people at the new address (
SLOTSERVE_PUBLIC_URL, DNS). Everyone's SSH known_hosts entries change with the address; the host keys themselves came with the bundle.
First admin and setup code¶
A new installation has no admin. Until one exists, SlotServe prints a one-time setup code in its log at every start; the sign-up form asks for it instead of the invite code, and that account becomes the admin:
docker compose logs slotserve | grep -A4 "No admin account"
docker compose exec slotserve slotserve setup-code
The code stops working as soon as an admin exists, and installations that already have an admin never ask for it.
While the invite code is still change-me, nobody else can sign up: set Invite code in Admin → System
settings, or create invite links in Admin → Users (they expire, count their sign-ups, can be revoked and can
ask for an admin's approval; they never contain the invite code).
More admins: Admin → Users, or docker compose exec slotserve slotserve set-admin <username> (--revoke to
remove). Keep at least two admins so one can help the other.
Reverse proxy¶
Put SlotServe behind a reverse proxy for HTTPS or a nicer address. Three things matter.
1. The address: Host or SLOTSERVE_PUBLIC_URL. SlotServe refuses changing requests (POST, PUT, PATCH,
DELETE) from a browser unless their Origin is SlotServe's own address: the host the request was sent to (Host,
or X-Forwarded-Host) or the origin of SLOTSERVE_PUBLIC_URL. A proxy that rewrites Host to 127.0.0.1:8000
makes every change in the web UI fail with "did not come from a SlotServe page". Pass the original host, and set
SLOTSERVE_PUBLIC_URL to the address people type (it is also used in links SlotServe sends):
2. Client addresses: SLOTSERVE_FORWARDED_ALLOW_IPS. Sign-in throttling locks an account per address
(account + IP), the audit log records addresses and API keys show where they were last used. SlotServe only
believes X-Forwarded-For from the proxies listed in SLOTSERVE_FORWARDED_ALLOW_IPS (default 127.0.0.1).
With Docker, the proxy's connections arrive from Docker's bridge address (for example 172.18.0.1), not
127.0.0.1: sign in once, look at the address of your sign-in in Admin → Audit log, and if it is the proxy's
rather than your own, put that address in:
SLOTSERVE_FORWARDED_ALLOW_IPS=172.18.0.1 # comma separated; "*" only if port 8000 is reachable from the proxy alone
Without it, everybody shares the proxy's address and failed sign-ins from one person can slow down others.
3. Streaming and the second port. Turn off buffering so model answers stream, allow long requests, and pass
WebSockets. Dev environment web interfaces use port 8001 and need an address of their own (they must stay a
different origin from SlotServe): a second server block or a second host name, with SLOTSERVE_DEVENV_WEB_URL
set to it.
server {
listen 443 ssl;
server_name gpu.example.org;
# ssl_certificate ...;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_read_timeout 1h;
client_max_body_size 0;
}
}
server {
listen 8443 ssl; # or another server_name on 443
server_name gpu.example.org;
# ssl_certificate ...;
location / {
proxy_pass http://127.0.0.1:8001;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_read_timeout 1h;
}
}
The OpenAI-compatible API (/v1) works through the same proxy; clients use https://gpu.example.org/v1. SSH and
direct ports of dev environments do not go through the proxy (see Ports and firewall).
Health checks and slotserve doctor¶
One set of checks feeds every view of SlotServe's health:
| Where | What | Who |
|---|---|---|
| Admin → Overview | Setup checklist, then the System health table: every check with its evidence, buttons, a diagnosis for recognised errors, and dismissing with a reason | Admins |
GET /healthz |
Shallow: the process answers (Docker's HEALTHCHECK uses it) |
Anyone |
GET /api/health |
Every check as JSON (checks[], status: ok / degraded / down); ?strict=true answers 503 while a check fails, for uptime monitors |
Admin session or the metrics token (Authorization: Bearer <SLOTSERVE_METRICS_TOKEN>) |
GET /metrics |
Prometheus: slotserve_check_status, slotserve_backup_age_seconds and the GPU and usage metrics |
Metrics token |
slotserve doctor |
The same checks from the command line | Shell on the server |
docker compose exec slotserve slotserve doctor # while the server runs
docker compose run --rm --no-deps slotserve slotserve doctor # when it does not start
slotserve doctor --json # machine-readable, secrets removed
doctor prints one line per check (ok, info, WARN, ERROR) with the evidence of warnings and errors, and
exits with code 1 when a check is at level error. It runs in its own process, so checks of the running server's
internals (lanes, background tasks) are only in the web UI and /api/health.
| Check | What it looks at | When it is not OK |
|---|---|---|
| database | The database can be written | Disk full or read-only, file permissions |
| disk | Free space where data and models live, and on storages | Disk full |
| engine | The inference engine program exists | Fix LLAMA_CPP_DIR / SLOTSERVE_LLAMA_SERVER_CMD; see Configuration |
| lanes | Every lane's model server | A failed load, with its diagnosis; FAQ: diagnoses |
| models | At least one model, an idle default per lane | Add a model, set a default (Admin → GPUs & lanes) |
| gpu | nvidia-smi sees GPUs |
GPUs gone |
| docker | Docker is reachable (dev environments on) | Dev environments: Docker |
| storage | Every storage mounts and is writable | Storage, the diagnosis on the check |
| chat | Chat platforms are connected | Admin → Chat channels |
| backup | The last backup and its copy off the machine | Backups |
| backup_age | The newest backup is younger than 36 hours | When a backup fails |
| restore | A restore waits for its reconciliation | The reconciliation report |
| migration | The last upgrade kept every row | data/migration_report.json |
| tasks | Background tasks run and report back | Restart SlotServe; report the evidence |
| secrets | Stored secrets can be decrypted | Secret key lost |
| security | Number of admins; dev environments mean root on the host | A note to read once |
| invite | The invite code is not change-me |
Set an invite code or use invite links |
Some results are notes rather than problems (no chat platform on purpose, a machine without GPU): Dismiss… them with a reason. A dismissed check stays in the table, no longer counts as a problem, and the reason is in the audit log; it comes back on its own when its level changes.
Inbox and alerts¶
Every notification goes into the in-app inbox first (the bell in the header) and then to the chat platforms the person linked. Members get the news about their own work (approvals, reminders, environments that are ready or failed). Admins also get the admin messages: approvals to give, sign-ups waiting, failed model loads, storages that stop working, sensitive settings changes, and the health alerts.
- A check that reaches its alert level (an error, for some checks a warning, some only after a while) sends one alert to all admins, and one more when it recovers. There are no repeats, also not across restarts. Dismissed checks do not alert.
- Group webhooks (Feishu / Lark, DingTalk, WeCom) can get admin alerts too: Admin → Chat channels.
- Without any chat platform nothing is lost: the inbox has everything. The Admin menu item shows how many admin messages are unread.
- For monitoring from outside, poll
/api/health?strict=trueor scrape/metrics(slotserve_check_status).
Container hardening and compatibility mode¶
Every container SlotServe starts for a dev environment is hardened in one fixed last step:
- no new privileges: setuid programs such as
sudocannot gain rights; - capabilities
NET_RAW,MKNOD,SYS_CHROOT,AUDIT_WRITEandSETFCAPare dropped (pingdoes not work); - at most
SLOTSERVE_DEVENV_PIDS_LIMITprocesses (default 4096), so a fork bomb stays inside its container; - a memory limit (the Memory limit setting, or with 0 the host's memory less max(4 GB, 10 %)), so an out-of-memory job is killed inside its container instead of taking SlotServe down;
- bounded logs, exactly the lane's GPUs, never privileged, never the host's network or process namespace, never the Docker socket.
People are root inside their container, so apt install works without sudo. What changes:
| Symptom inside an environment | Why | What to do |
|---|---|---|
sudo: effective uid is not 0 or a setuid program fails |
No new privileges | Run as root without sudo. For an image that must use setuid (its own init system, a user switch), an admin turns on Compatibility mode in that template's settings (Dev environments, admins only; the built-in desktop already uses it). Less isolated: only for images you trust. |
ping: permission denied |
NET_RAW is dropped |
Use curl or nc to test connections. |
fork: Resource temporarily unavailable |
The process limit | Fewer parallel workers, or raise SLOTSERVE_DEVENV_PIDS_LIMIT. |
A job is Killed |
The memory limit | A smaller batch, or a higher Memory limit. Admins see the limit that applies in /api/devenv (hardening). |
Web interface compatibility mode (Admin → System settings, SLOTSERVE_DEVENV_WEB_COMPAT) is a different
switch: it relaxes the rules that keep the pages of one environment from acting in another one opened in the same
browser (on the web port 8001). Turn it on only when an app in an environment's web interface does not work and
nothing else helps; it weakens the isolation between environments and nothing else.
Ports and firewall¶
| Port | What | Who needs to reach it |
|---|---|---|
8000 (PORT) |
Web UI, /api, the OpenAI-compatible /v1, /metrics |
Everyone (or only the reverse proxy) |
8001 (DEVENV_WEB_PORT, SLOTSERVE_DEVENV_WEB_PORT) |
Dev environment web interfaces (VS Code, JupyterLab, desktop, your own web apps) | Everyone who uses them (or only the reverse proxy) |
8002 (SLOTSERVE_DEVENV_GATEWAY_PORT) |
The container-side gateway: only /v1, for code inside dev environments |
Only the dev environment containers; never open it to the network |
| 2200-2299 (SSH ports) | SSH into dev environments, one port per running environment | Everyone who uses SSH |
| 2300-2399 (Direct port range) | Ports people publish directly from their environments | Everyone who uses them; a direct port has no SlotServe login |
- The ranges are settings (SSH ports, Direct port range); keep them apart from each other and from ports the host uses.
-
Docker publishes the SSH and direct ports itself, through its own iptables rules, which bypass
ufwandfirewalldrules on the host. To restrict who can reach them, use theDOCKER-USERchain or a firewall in front of the machine, for example: -
Port 8002 needs nothing when SlotServe runs in Docker: the gateway lives inside SlotServe's container and is reached as
slotserve-gwon each environment's network. When SlotServe runs directly on the host, it listens on Docker's bridge address (docker0); a host firewall that drops traffic from containers to the host must allow TCP 8002 from Docker's networks (by default inside172.16.0.0/12and192.168.0.0/16), for exampleufw allow from 172.16.0.0/12 to any port 8002 proto tcp. - Behind a reverse proxy, 8000 and 8001 can be bound to
127.0.0.1(ports: - "127.0.0.1:8000:8000") so that only the proxy reaches them.
Runbooks¶
Forgotten admin password or locked out¶
Symptom. An admin cannot sign in; or "too many attempts".
Diagnose. Failed sign-ins lock an account for 15 minutes from the address they came from (10 failures per
account and address, 30 per address); other addresses are not affected. Behind a proxy without
SLOTSERVE_FORWARDED_ALLOW_IPS, everyone shares one address (Reverse proxy).
Fix.
- Another admin resets the password in Admin → Users.
-
On the server:
docker compose exec slotserve slotserve reset-password <username> # asks for the new password docker compose exec slotserve slotserve set-admin <username> # if no admin is leftThe reset signs that account out everywhere. The lock is held in memory:
docker compose restart slotserveclears it. - No admin account at all any more: setup code.
Prevent. Two admins; passwords in a password manager.
Disk full¶
Symptom. The health check disk warns or errors; backups, downloads or model loads fail with "No space left on device"; the database cannot be written.
Diagnose.
df -h # which file system
du -sh data/* | sort -h | tail # SlotServe's data directory
docker system df # images, containers, volumes, build cache
du -sh "$MODELS_DIR"
Usual culprits: models, Docker images (environment templates, saved images, kept environments' snapshots),
/workspace volumes, old backups and upgrade snapshots in data/backups/, logs.
Fix.
- Models no one uses: delete them in Models (removes the file).
- Docker:
docker image prune(unused images), and saved environment images people no longer need (deleting the saved template removes its image). Do notdocker volume pruneblindly: volumes hold people's/workspace. - Backups: upgrade snapshots and
prerestorebundles are never removed automatically; delete old ones by hand once you no longer need them. Lowerbackup_keepif needed. - Logs: model server logs rotate at 20 MB, the SlotServe container's Docker log at 5 × 50 MB. Containers of dev environments are limited the same way.
- Usage records are folded after
SLOTSERVE_USAGE_RETENTION_DAYS; the audit log is kept forSLOTSERVE_AUDIT_RETENTION_DAYS.
Prevent. Models and Docker's data root on a different disk than the system; a backup target; set a soft limit on personal storages; watch the disk check's warning (it starts at twice the download free-space minimum, at least 5 GB).
Dev environment does not start¶
Symptom. An environment's card says it did not start; the person was notified.
Diagnose. As admin, Dev environments → Everyone, open the environment: its error and Logs. Recognised errors come with a diagnosis (cause and next steps); see FAQ: diagnoses. Then by cause:
| Cause | Typical error | Fix |
|---|---|---|
| Image pull | pull access denied, manifest unknown, toomanyrequests |
Check the image name and tag; the Docker host's access to the registry (proxy, mirror); docker login on the host for private images or Docker Hub limits; pull it once by hand. |
| Port conflict | port is already allocated, address already in use |
Something else on the host uses a port of the SSH or direct range: move the range (settings) or the other program. |
| Mounts | failed to mount local volume, mount error(13), access denied by server |
Storage: Check it in Admin → Storage; install nfs-common / cifs-utils on the Docker host; NFS exports must allow the host. Storage directory: must be a path on the Docker host. |
| CPU set | Requested CPUs are not available, cpuset |
Fewer cores than lanes reserve: lower CPU cores reserved per GPU; after a CPU change restart SlotServe so it reads the host again. |
| GPU | could not select device driver "nvidia" |
GPUs gone |
| Lane without GPUs | devenv_lane_no_gpus |
Give the lane its GPUs in Admin → GPUs & lanes. |
| Docker | Cannot connect to the Docker daemon |
Docker running? Socket mounted? See Dev environments: Docker. |
Fix. Fix the cause, then Try again on the environment.
Prevent. Run python -m tools.verify after changes to Docker, drivers or templates (Verify);
prefer images from registries you control for important templates.
GPUs gone after a driver or Docker upgrade¶
Symptom. The health check gpu says "No GPU detected"; models fail with CUDA errors; dev environments fail
with could not select device driver "nvidia".
Diagnose.
nvidia-smi # on the host: does the driver work?
docker run --rm --gpus all ubuntu nvidia-smi # can Docker give containers a GPU?
docker compose exec slotserve nvidia-smi # does SlotServe's container see them?
Fix.
nvidia-smifails on the host: the kernel module does not match the new driver; reboot, or reinstall the driver for the running kernel.-
Only Docker fails: reconfigure the NVIDIA Container Toolkit and restart Docker:
-
Models fail with "CUDA driver version is insufficient": the engine needs a newer driver, or rebuild it for the installed CUDA; with Docker the CUDA major of
CUDA_IMAGEmust match yourllama-serverbuild. - Then
docker compose up -d(a Docker restart stops SlotServe unlessrestart: unless-stoppedbrings it back).
Prevent. Pin the driver packages (apt-mark hold), upgrade drivers and Docker in a maintenance window, and run
slotserve doctor and tools/verify afterwards.
Suspected account or key leak¶
Symptom. Unknown sign-ins, API usage nobody recognises, a key or password posted somewhere.
Diagnose.
- Admin → Audit log: filter by the person, by action (
auth.*for sign-ins and failed sign-ins,key.*for API keys) and dates; addresses are recorded (see Reverse proxy for real addresses). Export the selection as CSV for the record. - Admin → Request log: requests by person, key and source (API key, environment key, Playground); each API key shows the address it was last used from.
Fix.
- Disable the account (Admin → Users) if it is compromised: it is signed out everywhere and its bookings end.
- Revoke API keys (Admin → API keys, or the person under Access credentials). Environment keys end with their environment.
- Sign out everywhere: the person uses Settings → Signed-in devices → Sign out all other devices; a password change or an admin's reset does the same.
- Remove SSH keys the person does not recognise (Access credentials) and set a new environment password.
- If an admin account or the server's
.envleaked, also rotate the secret key and change the invite code, chat platform tokens andSLOTSERVE_METRICS_TOKEN.
Prevent. Keys with an expiry, invite links instead of a shared invite code, few admins, and review the audit log now and then.
Verify on real hardware¶
tools/verify checks a running installation end to end, through its public API, the way people use it: a GPU
booking and an OpenAI-style request, every built-in dev environment template with SSH (key and password) and its
web interface, an environment without GPU, a storage, the container hardening and the security protections. It
creates a throw-away account and removes it again; it changes no settings (a feature that is off is reported as
skipped). Run it after installing, after upgrades of SlotServe, drivers or Docker, and for every release candidate.
From a checkout of the SlotServe repository on a machine that reaches the server (Python 3.11+ with httpx, an
OpenSSH client; sshpass for the password logins):
The admin password is asked for (or SLOTSERVE_VERIFY_PASSWORD). Useful options: --lane, --model,
--templates pytorch,vscode (a subset), --storage-id or --storage-host-path, --no-ssh, --no-web,
--save-image, --destructive (also a fork bomb in an environment without GPU), --keep (keep the test account
for debugging), --insecure (self-signed TLS). Pulling every template image takes a while the first time.
It writes verify-report-<host>-<version>.md and .json. Results are pass, fail, skip (a feature is
off or a tool is missing), pending or manual: the manual ones are browser checks the tool cannot do (a
CSRF proof-of-concept page written next to the report, and the isolation of two environments' web pages), with
instructions in the report. Exit code 0 means nothing failed, 1 that a check failed, 2 that the server could not be
reached or signed in to. Attach the report to the release pull request (see the release checklist).