Skip to content

Commit f6498c3

Browse files
chapterjasonclaude
andcommitted
Claim volume mountpoints via image entrypoint, stop destroying shared state
Fresh Docker named volumes mount as root:root under sysbox-runc even when the image path is pre-owned, so ~/projects was unwritable on first start. Chown it (plus /mnt/home-persist, /mnt/shared) from a real entrypoint script that execs into /sbin/init, replacing the Terraform-side sudo dance. Also drop stale docker_volume.shared / docker_volume.home_persist addresses from existing workspaces' tfstate via `removed { destroy = false }`, so prior-template workspaces stop trying to delete the real shared volumes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 19b1f86 commit f6498c3

4 files changed

Lines changed: 74 additions & 31 deletions

File tree

main.tf

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,6 @@ resource "coder_agent" "main" {
100100
echo "${data.coder_workspace_owner.me.ssh_private_key}" > ~/.ssh/id_ed25519
101101
chmod 600 ~/.ssh/id_ed25519
102102
103-
# Per-owner persistence volume. home-persist-resolve (run via
104-
# coder_script.lifecycle_init below) symlinks declared $HOME paths into it.
105-
sudo mkdir -p /mnt/home-persist
106-
sudo chown "${local.workspace_uid}:${local.workspace_gid}" /mnt/home-persist
107-
108-
# Deployment-wide shared drop box. Sticky-bit 1777 (like /tmp) so anyone
109-
# can write but only the file's owner can delete.
110-
sudo mkdir -p /mnt/shared
111-
sudo chmod 1777 /mnt/shared
112103
EOT
113104

114105
shutdown_script = ""
@@ -349,6 +340,27 @@ resource "coder_script" "lifecycle_init" {
349340
EOT
350341
}
351342

343+
# Drop leftover state entries from pre-c56cc1d templates without deleting the
344+
# underlying Docker volumes. Existing workspaces still carry
345+
# docker_volume.shared / docker_volume.home_persist in their tfstate; on every
346+
# stop/destroy Terraform tries to remove the real volumes (which hold shared
347+
# and per-owner persistent data). These `removed` blocks tell Terraform to
348+
# forget the addresses on the next apply while leaving the volumes intact.
349+
# Safe to delete once no workspace has those addresses in state anymore.
350+
removed {
351+
from = docker_volume.shared
352+
lifecycle {
353+
destroy = false
354+
}
355+
}
356+
357+
removed {
358+
from = docker_volume.home_persist
359+
lifecycle {
360+
destroy = false
361+
}
362+
}
363+
352364
# Persistent storage for the workspace's inner dockerd. Without this, every
353365
# `docker pull`, buildx cache, and image built inside the workspace is lost
354366
# on every restart. The workspace's dockerd runs under sysbox-runc.

src/base/Dockerfile

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -96,28 +96,14 @@ USER root
9696
# Coder agent: systemd unit runs /etc/coder/agent-init.sh as the workspace
9797
# user after dockerd is up. The script itself is uploaded at container-create
9898
# time by the Terraform template (kreuzwerker/docker `upload` block).
99-
RUN mkdir -p /etc/coder && \
100-
cat > /etc/systemd/system/coder-agent.service <<EOF
101-
[Unit]
102-
Description=Coder Agent
103-
After=docker.service network-online.target
104-
Wants=network-online.target docker.service
105-
ConditionPathExists=/etc/coder/agent-init.sh
106-
107-
[Service]
108-
Type=simple
109-
User=${USERNAME}
110-
Group=${USERNAME}
111-
WorkingDirectory=/home/${USERNAME}
112-
PassEnvironment=CODER_AGENT_TOKEN
113-
ExecStart=/bin/bash /etc/coder/agent-init.sh
114-
Restart=on-failure
115-
RestartSec=5
116-
117-
[Install]
118-
WantedBy=multi-user.target
119-
EOF
120-
99+
RUN mkdir -p /etc/coder
100+
COPY coder-agent.service /etc/systemd/system/coder-agent.service
121101
RUN systemctl enable coder-agent.service
122102

103+
# Entrypoint claims fresh-volume mountpoints for the workspace user before
104+
# systemd starts. See entrypoint.sh for rationale.
105+
COPY --chmod=0755 entrypoint.sh /usr/local/sbin/coder-entrypoint
106+
ENV USERNAME=${USERNAME}
107+
108+
ENTRYPOINT ["/usr/local/sbin/coder-entrypoint"]
123109
CMD ["/sbin/init"]

src/base/coder-agent.service

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[Unit]
2+
Description=Coder Agent
3+
After=docker.service network-online.target
4+
Wants=network-online.target docker.service
5+
ConditionPathExists=/etc/coder/agent-init.sh
6+
7+
[Service]
8+
Type=simple
9+
User=coder
10+
Group=coder
11+
WorkingDirectory=/home/coder
12+
PassEnvironment=CODER_AGENT_TOKEN
13+
ExecStart=/bin/bash /etc/coder/agent-init.sh
14+
Restart=on-failure
15+
RestartSec=5
16+
17+
[Install]
18+
WantedBy=multi-user.target

src/base/entrypoint.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/bash
2+
# Container entrypoint. Runs as root before systemd (PID 1) starts.
3+
#
4+
# Fresh Docker named volumes mount as root:root the first time they're
5+
# attached, even though the underlying image path is owned by the workspace
6+
# user (docker's "copy-up" doesn't reliably apply under sysbox-runc). Claim
7+
# the mountpoints here so coder-agent.service — which starts later under
8+
# systemd — sees correctly-owned mounts. Idempotent: chown/chmod are no-ops
9+
# when values already match.
10+
set -e
11+
12+
USERNAME="${USERNAME:-coder}"
13+
14+
for path in "/home/${USERNAME}/projects" /mnt/home-persist; do
15+
[ -d "$path" ] || continue
16+
chown "${USERNAME}:${USERNAME}" "$path"
17+
chmod 0755 "$path"
18+
done
19+
20+
# Deployment-wide share: every workspace runs as the same uid, so full 0777
21+
# gives all workspaces unrestricted read/write/delete on everything inside.
22+
if [ -d /mnt/shared ]; then
23+
chown "${USERNAME}:${USERNAME}" /mnt/shared
24+
chmod 0777 /mnt/shared
25+
fi
26+
27+
exec "$@"

0 commit comments

Comments
 (0)