侧边栏壁纸
  • 累计撰写 110 篇文章
  • 累计创建 54 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

OpenCode Web Deployment — Notes

FlyingEagle
2026-08-04 / 0 评论 / 0 点赞 / 1 阅读 / 3,694 字

OpenCode Web Deployment — Notes

Architecture

  • Container: debian:bookworm-slim, user opencode (uid 1000), SSH + web (opencode web --hostname 0.0.0.0 --port 4096), named volume opencode-workspace -> /home/opencode/workspace.
  • Compose prefixes volume names: opencode-workspace becomes opencode_opencode-workspace (project dir opencode). Pin with name: opencode-workspace in volumes: if you want the plain name.

URL routing (core gotcha)

  • / = launcher/home -> cannot create a new session here (no project context).
  • /{base64(dir)} = project home, New session works.
  • /{base64(dir)}/session and /session/{id} -> new / existing sessions.
  • L2hvbWUvb3BlbmNvZGUvd29ya3NwYWNl = base64 of /home/opencode/workspace.
  • Re-encode a path with: printf '/path' | base64 -w0.

Web UI auth

  • Env OPENCODE_SERVER_PASSWORD=... (required for network access; without it the server is unsecured). Optional OPENCODE_SERVER_USERNAME (default opencode).
  • Set in compose environment: (do not bake into the image).

Caddy reverse proxy

@opencode host opencode.domain.com
handle @opencode {
    basic_auth {
        user $2a$14$HASH     # from: caddy hash-password (NOT passwd#hash)
    }
    redir / /L2hvbWUvb3BlbmNvZGUvd29ya3NwYWNl permanent
    reverse_proxy opencode-sandbox:4096 {
        flush_interval -1
    }
}
  • redir / matches only the exact root path -> encoded path and /session/* are proxied unchanged (no loop).
  • flush_interval -1 disables buffering -> smooth SSE streaming. Recommended, not strictly required.
  • Generate the hash with caddy hash-password (user passwd#hash is invalid Caddy).

Volumes / persistence (btrfs host)

  • Named volume data: /var/lib/docker/volumes/opencode_opencode-workspace/_data. It is a plain directory on the root filesystem, so df shows /dev/vda1 — this is normal, not a bug.
  • Ownership: files owned by uid 1000 (opencode). Any container sharing the volume must run as uid 1000, or mount :ro.
  • Bind mount alternative: map /root/workspace:/home/opencode/workspace, then on the host run chown 1000:1000 /root/workspace and git init -q (the image’s git init is shadowed by the bind mount). No Docker integrity damage.
  • Share the volume with another container (e.g. hermes): same compose file mounts the same volume; across projects use external: true + name: opencode_opencode-workspace. Keep uid 1000 or use :ro.
    • Flow: opencode builds in the volume -> hermes publishes from the volume to the website.

Sessions / tasks

  • Closing the browser does not stop a running task — it runs server-side. Reopen the page to review history / live state.
  • A task dies only if the container is stopped or the host reboots mid-task.
  • Chat/session history lives in /home/opencode/.local/share/opencode (the container writable layer, NOT the volume). It survives host reboot and docker restart, but is lost on container recreate (docker compose up -d --build).
  • Fix: add a second volume opencode-data:/home/opencode/.local/share/opencode to preserve history.

Backup to WebDAV via cron (rclone)

# setup (once)
apt install -y rclone
rclone config      # create remote "backup:" with type=webdav
# /root/backup-workspace.sh
#!/bin/bash
set -euo pipefail
TMP=/tmp/opencode.tar.gz
DEST="backup:dockervolumes/opencode-$(date +%F_%H%M).tar.gz"
docker run --rm -v opencode_opencode-workspace:/data alpine tar czf - -C /data . > "$TMP"
rclone copyto "$TMP" "$DEST"
rclone delete backup:dockervolumes/ --min-age 14d
rm -f "$TMP"
echo "backup ok -> $DEST" >> /var/log/opencode-backup.log
chmod +x /root/backup-workspace.sh
# cron (as root)
0 2 * * * /root/backup-workspace.sh >> /var/log/opencode-backup.log 2>&1
  • Use rclone copyto (not rcat) for WebDAV reliability.
  • Keep rclone.conf outside the container (host root home) and encrypt it with an rclone password.
  • Back up both volumes (workspace + data).
  • Test the restore path once:
    docker run --rm -v opencode_opencode-workspace:/data -v $(pwd):/backup alpine \
      sh -c "cd /data && tar xzf /backup/<file>.tar.gz"
    
  • Snapshot consistency: docker stop does not trigger restart: unless-stopped (that policy only applies to crashes/reboots), so stop -> dump -> start is the clean snapshot method. Alternatively docker pause / docker unpause for lower downtime.
0

评论区