On Linux, SnowLuma runs only via Docker. The image bundles Linux QQ + Xvfb + VNC + noVNC + supervisord + the SnowLuma lite distribution, so the host needs nothing beyond Docker.
The image build scripts and Compose template live in a separate repo: SnowLuma/SnowLuma.Docker.Framework.
| Item | Value |
|---|---|
| Image | motricseven7/snowluma:latest (or a specific tag, e.g. :v1.10.0) |
| Arch | linux/amd64, linux/arm64 |
| Base | node:22-bookworm-slim |
| Bundled | Linux QQ, Xvfb, VNC, noVNC, supervisord, SnowLuma lite |
The image does not rebuild SnowLuma — it consumes the prebuilt lite tarball from each SnowLuma GitHub Release, so the release and the image build are decoupled.
| Port | Purpose |
|---|---|
5900 |
VNC (remote desktop — needed to scan-login QQ) |
6081 |
noVNC (browser VNC, no client needed) |
5099 |
SnowLuma WebUI |
3000 |
OneBot HTTP (default) |
3001 |
OneBot WebSocket (default) |
--cap-add=SYS_PTRACE and --security-opt seccomp=unconfined are still required.
SnowLuma's native addon uses ptrace to inject the hook into the in-container QQ process. The official image grants cap_sys_ptrace to /usr/local/bin/node, so SnowLuma can run as the unprivileged snowluma user while still injecting within the container's capability boundary.
With current official images, you no longer need to change the host's kernel.yama.ptrace_scope. Keep these container options instead:
To verify the file capability inside the container:
If an old image still asks you to relax the host sysctl, upgrade the image first. Changing kernel.yama.ptrace_scope should only be a temporary troubleshooting fallback when upgrading is not possible.
--cap-add=SYS_PTRACE and --security-opt seccomp=unconfined are mandatory. SnowLuma's native addon uses ptrace to inject the hook into the QQ process; the default seccomp profile blocks that.
--shm-size=1g is needed for QQ's Chromium runtime — the default container /dev/shm is too small and QQ will crash.
Recommended over docker run. The repo's docker-compose.yml:
Start:
Upgrade:
| Var | Default | Notes |
|---|---|---|
VNC_PASSWD |
vncpasswd |
VNC / noVNC login password. Change this, otherwise anyone can see your QQ desktop. |
TZ |
Asia/Shanghai |
Container timezone. Set in Dockerfile, overridable via env. |
SNOWLUMA_UID |
1000 |
uid of the in-container snowluma user. Match it to the host owner of your mounted volumes. |
SNOWLUMA_GID |
1000 |
gid of the in-container snowluma user. |
SNOWLUMA_WEBUI_PORT |
5099 |
WebUI listen port (inside the container). |
SNOWLUMA_WEBUI_HOST_PORT |
5099 |
WebUI host mapping port (defaults to same as container port). |
SNOWLUMA_LOG_LEVEL |
info |
error / warn / info / debug. |
SNOWLUMA_SCREEN |
1920x1080x24 |
Xvfb resolution + color depth. |
SNOWLUMA_HOOK_AUTOLOAD |
1 |
Image opts into auto-injection by default. Set 0 to fall back to the manual Load workflow. |
SNOWLUMA_EXTRA_QQ_HOMES |
empty | Comma- or space-separated extra QQ /app/... HOME paths for auto-starting multiple accounts. |
SNOWLUMA_QQ_FLAGS |
--disable-gpu --disable-software-rasterizer --disable-gpu-compositing |
Flags passed to Linux QQ. The default disables GPU / SwiftShader software rendering to prevent memory leaks. |
VNC_PORT |
5900 |
VNC host mapping port. |
NOVNC_PORT |
6081 |
noVNC host mapping port. |
ONEBOT_HTTP_PORT |
3000 |
OneBot HTTP host mapping port. |
ONEBOT_WS_PORT |
3001 |
OneBot WebSocket host mapping port. |
Environment variables override runtime.json, so they're the ergonomic knob in Docker.
| Volume | Container path | Contents |
|---|---|---|
snowluma-data |
/app/snowluma-data |
SnowLuma config, cache, SQLite. Includes config/onebot.json and config/runtime.json. |
snowluma-qq-config |
/app/.config |
QQ client config. |
snowluma-qq-data |
/app/.local/share |
QQ user data (login state, cache). |
Don't delete these on upgrade — you'll lose login state and configuration.
http://<host>:6081/ (noVNC) and enter VNC_PASSWD.http://<host>:5099/ for the SnowLuma WebUI.On a fresh data volume the first launch logs a one-time password:
Just the password string:
The bootstrap password is logged only on a brand-new data volume's first boot. Reusing the volume or restarting won't regenerate it.
Linux QQ ships a single-instance lock — clicking the QQ icon again on a desktop that already has one running just focuses the existing window, it doesn't start a second process. The lock file lives at $HOME/.config/QQ/SingletonLock, so the solution is to give each instance its own HOME.
SnowLuma needs no extra configuration: HookManager walks every QQ main process it can find, injects the hook into each, and spins up a separate OneBotInstance per UIN with its own config/onebot_<uin>.json.
Mount one dedicated volume per extra account, then list the corresponding container paths in SNOWLUMA_EXTRA_QQ_HOMES. The list can be comma- or space-separated:
On boot, the container generates one supervisor program per extra HOME. Each extra QQ runs as the snowluma user with the same DISPLAY=:1 and the same SNOWLUMA_QQ_FLAGS, so injection won't fail because a manual docker exec accidentally launched QQ as root.
Check process status:
For a one-off launch, keep the user and environment explicit. The important bit is: do not run the extra QQ as root.
If /app/qq-acct2 is not mounted to a volume, recreating the container loses that account's login state. For long-term use, prefer the Compose setup above.
HOME between two QQs; they'll fight over the lock file.--shm-size=1g is fine for one; bump to 2gb+ for multi-account.get_login_info returns the new UINs once login completes.fluxbox, not an XFCE/GNOME session. ~/.config/autostart/*.desktop may not be read. Let supervisor handle multi-account boot instead.The image defaults to SNOWLUMA_HOOK_AUTOLOAD=1: when the container starts, the hook is injected into the QQ process in passive-observe mode. Once you scan-login, the hook auto-promotes to working mode. supervisor also restarts QQ on crash through the same flow.
If you prefer the legacy "manual Load via WebUI" workflow:
Or set SNOWLUMA_HOOK_AUTOLOAD: 0 in docker-compose.yml, or edit /app/snowluma-data/config/runtime.json inside the volume to "hookAutoLoad": false. Environment variables win.
Shell into the container:
Follow logs:
Just SnowLuma's own logs (filter out QQ's noisy output):
Browse the data directory:
Edit OneBot config and restart:
If a host port is taken, just shift the host side; container ports stay the same. Example moving OneBot HTTP to host 8080:
Or via env (the Compose template already supports it):
Useful when you need an unreleased SnowLuma build:
Or let the script pull the latest release:
Multi-arch manifests are merged by CI (.github/workflows/docker-image.yml); the local script only builds a single platform.
SYS_PTRACE and seccomp=unconfined. Only run on trusted hosts.VNC_PASSWD (vncpasswd) must be changed before exposing the container to anything other than localhost — otherwise your QQ desktop is wide open.