feat(docker): bind the data volumes to /media/disk1/containers/volcanic_secretary_core

The entrypoint chowns the three mount roots before dropping to app, since
Docker creates missing bind directories as root.
This commit is contained in:
EugeneTes
2026-09-29 09:40:28 +00:00
parent 8014a0e770
commit 28c6f90d91
3 changed files with 17 additions and 14 deletions

View File

@@ -65,7 +65,8 @@ RUN if [ "$INSTALL_PLAYWRIGHT" = "true" ]; then \
&& rm -rf /var/lib/apt/lists/*; \
fi
# The entrypoint hard-codes uid/gid 1654. Named volumes copy this ownership on first mount.
# The entrypoint hard-codes uid/gid 1654. Named volumes copy this ownership on first mount; bind-mounted roots are
# chowned by the entrypoint (not recursively), since Docker creates missing host directories as root.
RUN test "$(id -u app)" = 1654 && test "$(id -g app)" = 1654 \
&& mkdir -p /data/workspaces /data/agent /data/claude /home/app \
&& chown -R app:app /data /home/app
@@ -85,4 +86,5 @@ ENV Telegram__WorkspaceRoot=/data/workspaces \
# No USER: PID 1 (docker-init with compose's init: true) must stay root, or the agent could read every secret from
# /proc/1/environ. setpriv drops to app and execs dotnet, so Environment.ProcessPath is the muxer the bridge needs.
# An empty bounding set and no_new_privs keep the agent's processes from regaining root through setuid binaries.
ENTRYPOINT ["setpriv", "--reuid=1654", "--regid=1654", "--init-groups", "--inh-caps=-all", "--bounding-set=-all", "--no-new-privs", "--", "dotnet", "Volcanic.AgenticCore.Telegram.dll"]
# The shell only chowns the mount roots and execs setpriv, so the process tree is unchanged.
ENTRYPOINT ["/bin/sh", "-c", "chown 1654:1654 /data/workspaces /data/agent /data/claude; exec setpriv --reuid=1654 --regid=1654 --init-groups --inh-caps=-all --bounding-set=-all --no-new-privs -- dotnet Volcanic.AgenticCore.Telegram.dll"]

View File

@@ -32,12 +32,8 @@ services:
- PLAYWRIGHT_BROWSER=${PLAYWRIGHT_BROWSER:-}
- PLAYWRIGHT_EXTRA_ARGS=${PLAYWRIGHT_EXTRA_ARGS:---no-sandbox}
- PLAYWRIGHT_COMMAND=${PLAYWRIGHT_COMMAND:-playwright-mcp}
# Bind mounts on the host. Docker creates missing directories as root; the entrypoint hands the three roots to app.
volumes:
- workspaces:/data/workspaces
- agent:/data/agent
- claude:/data/claude
volumes:
workspaces:
agent:
claude:
- /media/disk1/containers/volcanic_secretary_core/workspaces:/data/workspaces
- /media/disk1/containers/volcanic_secretary_core/agent:/data/agent
- /media/disk1/containers/volcanic_secretary_core/claude:/data/claude

View File

@@ -129,15 +129,20 @@ The image sets these; normally leave them alone:
## Volumes
The compose file binds them to directories under `/media/disk1/containers/volcanic_secretary_core/` on the host
(`workspaces/`, `agent/`, `claude/`); edit the three `volumes:` lines to put them elsewhere.
| Volume | Path | Contents |
|---|---|---|
| `workspaces` | `/data/workspaces` | One directory per chat (`/data/workspaces/<chatId>`): `AGENTS.md` or `CLAUDE.md`, `PERSONA.md`, `MEMORY.md`, `inbox/` (received files), `browser/` (screenshots, downloads), project-level `.volcanic/extensions/`, the Google Calendar token (`.volcanic/google-calendar/token.json`) |
| `agent` | `/data/agent` | Sessions (`sessions/telegram-<chatId>/`), `settings.json`, `extensions/*.dll`, chat time zones (`telegram/chat-settings.json`), `scheduler/jobs.json` |
| `claude` | `/data/claude` | The Claude login (credentials and onboarding state) |
Named volumes survive redeploys. Coolify prefixes them with the resource's uuid and deletes them only when you delete
the resource together with its volumes. Bind mounts work too if the host directories belong to uid 1654
(`chown -R 1654:1654 <dir>`).
Bind mounts survive redeploys and deleting the resource. Docker creates missing host directories as root; the
entrypoint chowns the three mount roots (not their contents) to uid 1654 before it drops to `app`, so files copied in
from elsewhere need `chown -R 1654:1654 <dir>` on the host. Named volumes (`-v workspaces:/data/workspaces`, as in
the `docker run` example) work too. On an NTFS disk (`fuseblk`, as `/media/disk1` is) ownership and modes aren't
stored: everything shows as root with mode 777, `chown` is a silent no-op, and `app` can still write.
`HOME` (`/home/app`) isn't a volume: `~/.gitconfig`, `~/.ssh`, `gh` logins and the npm cache are lost on redeploy.
@@ -211,7 +216,7 @@ given under [First run and Claude login](#first-run-and-claude-login)).
| `[executable_missing]` in a reply, or `Claude Code (claude) was not found on PATH` at startup | The image has no working `claude`. Rebuild and check that `claude --version` ran in the build log |
| `[auth]` in a reply | The Claude login is missing or expired: `/claude-code-login`, or the `docker exec -u app` command above |
| `[mcp_startup]` in a reply | The bridge files are missing from `/app`: `Volcanic.AgenticCore.Extensions.ClaudeCode.Bridge.dll` and `Volcanic.AgenticCore.Extensions.ClaudeCode.Bridge.runtimeconfig.json`. The build checks for both, so rebuild from a clean tree |
| `Permission denied` under `/data` | Bind-mounted directories owned by another user: `chown -R 1654:1654 <dir>` on the host |
| `Permission denied` under `/data` | Files in the bind-mounted directories owned by another user: `chown -R 1654:1654 /media/disk1/containers/volcanic_secretary_core` on the host |
| `Telegram reports another poller or a webhook for this bot (409)` | Another instance polls with the same token (an old container, a local `dotnet run`), or the bot has a webhook. Stop the other instance; remove a webhook with `https://api.telegram.org/bot<token>/deleteWebhook` |
| `Telegram rejected the bot token (401 Unauthorized)`, restart loop | Wrong or revoked `TELEGRAM_BOT_TOKEN` |
| Browser tools missing or failing | The image was built with `INSTALL_PLAYWRIGHT=false`, `PLAYWRIGHT_ENABLED` is off, or `PLAYWRIGHT_EXTRA_ARGS` lost `--no-sandbox`. Chromium crashes also come from a small `/dev/shm`; keep `shm_size: 1gb` |