Skip to content

Commit a222fe1

Browse files
committed
fix(install): replace systemctl mask with TimeoutStartSec drop-in
systemctl mask symlinks docker.service to /dev/null, which causes systemctl preset to fail with 'No such file or directory' errors during dpkg postinst on Ubuntu — introducing new failures instead of solving the original hang. New approach: create a systemd drop-in that sets TimeoutStartSec=30, limiting how long systemd waits for dockerd to start. This: - Does not break systemctl preset (no symlink to /dev/null) - Catches ALL start attempts (dpkg postinst AND direct systemctl calls) - Is cleanly reversible (just remove the drop-in file) - Keeps policy-rc.d as secondary safety net Also removed proxy ARG/ENV from Dockerfile.
1 parent 901bd5c commit a222fe1

3 files changed

Lines changed: 59 additions & 55 deletions

File tree

docker/Dockerfile

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -90,23 +90,15 @@ ARG WEBSOFT9_PRODUCT_VERSION=
9090
ARG WEBSOFT9_APPSTORE_CHANNEL=release
9191

9292
# Restic backup binary — single static build, no runtime deps
93-
# Try Chinese mirror proxies first, GitHub direct last (slow/unreachable in China)
9493
RUN RESTIC_URL="https://github.com/restic/restic/releases/download/v0.17.3/restic_0.17.3_linux_amd64.bz2" \
95-
&& for url in \
96-
"https://mirror.ghproxy.com/${RESTIC_URL}" \
97-
"https://ghproxy.com/${RESTIC_URL}" \
98-
"https://ghproxy.net/${RESTIC_URL}" \
99-
"$RESTIC_URL"; do \
100-
echo "Trying: $url" \
101-
&& if curl -fsSL --retry 3 --retry-delay 5 --connect-timeout 30 --max-time 300 "$url" | bzip2 -d > /usr/local/bin/restic; then \
102-
chmod +x /usr/local/bin/restic \
103-
&& restic version \
104-
&& echo "Restic installed from: $url" \
105-
&& break; \
106-
fi; \
107-
echo "Failed: $url, trying next..."; \
108-
done \
109-
&& [ -x /usr/local/bin/restic ] || (echo "All Restic download sources failed" && false)
94+
&& echo "Downloading restic from: $RESTIC_URL" \
95+
&& curl -fsSL --retry 3 --retry-delay 5 --connect-timeout 30 --max-time 300 -o /tmp/restic.bz2 "$RESTIC_URL" \
96+
&& bzip2 -t /tmp/restic.bz2 \
97+
&& bzip2 -d -c /tmp/restic.bz2 > /usr/local/bin/restic \
98+
&& chmod +x /usr/local/bin/restic \
99+
&& restic version \
100+
&& rm -f /tmp/restic.bz2 \
101+
&& echo "Restic installed successfully"
110102

111103
COPY --from=npm-runtime /app /app
112104
COPY --from=npm-runtime /etc/letsencrypt.ini /etc/letsencrypt.ini

install/install_docker.sh

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -206,28 +206,40 @@ _cleanup_policy_rc_d() {
206206
}
207207

208208
# ---------------------------------------------------------------------------
209-
# Mask / unmask Docker systemd services.
210-
# systemctl mask creates a symlink to /dev/null, causing ALL start attempts
211-
# (both dpkg postinst via deb-systemd-invoke AND direct systemctl start
212-
# calls from get-docker.sh) to fail immediately instead of blocking.
213-
# This is the critical fix: policy-rc.d only blocks deb-systemd-invoke,
214-
# but get-docker.sh calls systemctl start docker directly after apt-get,
215-
# bypassing policy-rc.d entirely.
209+
# Limit Docker start timeout via systemd drop-in.
210+
# Docker's service file sets TimeoutStartSec=0 (infinite), so systemctl start
211+
# blocks forever if dockerd cannot initialize (common on Ubuntu with
212+
# cgroup v2 / AppArmor / iptables issues).
213+
#
214+
# We create a drop-in that sets TimeoutStartSec=30 — systemd will give up
215+
# after 30s instead of hanging. This is cleaner than systemctl mask
216+
# (which breaks systemctl preset and causes "No such file or directory"
217+
# errors) and more comprehensive than policy-rc.d (which only blocks
218+
# deb-systemd-invoke, not direct systemctl start calls from get-docker.sh).
216219
# ---------------------------------------------------------------------------
217-
_mask_docker_services() {
220+
_limit_docker_start_timeout() {
218221
if ! command_exists systemctl; then
219222
return 0
220223
fi
221-
log_info "Masking docker.service, containerd.service, docker.socket to prevent blocking starts"
222-
systemctl mask docker.service docker.socket containerd.service 2>/dev/null || true
224+
log_info "Limiting Docker start timeout to 30s via systemd drop-in"
225+
mkdir -p /etc/systemd/system/docker.service.d
226+
cat > /etc/systemd/system/docker.service.d/99-websoft9-timeout.conf <<'DROPOEOF'
227+
[Service]
228+
TimeoutStartSec=30
229+
DROPOEOF
230+
systemctl daemon-reload 2>/dev/null || true
223231
}
224232

225-
_unmask_docker_services() {
233+
_remove_docker_start_timeout() {
226234
if ! command_exists systemctl; then
227235
return 0
228236
fi
229-
log_info "Unmasking docker.service, containerd.service, docker.socket"
230-
systemctl unmask docker.service docker.socket containerd.service 2>/dev/null || true
237+
if [ -f /etc/systemd/system/docker.service.d/99-websoft9-timeout.conf ]; then
238+
rm -f /etc/systemd/system/docker.service.d/99-websoft9-timeout.conf
239+
rmdir --ignore-fail-on-non-empty /etc/systemd/system/docker.service.d 2>/dev/null || true
240+
systemctl daemon-reload 2>/dev/null || true
241+
log_info "Removed Docker start timeout limit"
242+
fi
231243
}
232244

233245
# ---------------------------------------------------------------------------
@@ -409,8 +421,8 @@ install_docker_custom() {
409421
# Prevent Docker from being started during installation — by ANY mechanism.
410422
# See install_docker_official for detailed rationale.
411423
_setup_policy_rc_d
412-
_mask_docker_services
413-
trap '_cleanup_policy_rc_d; _unmask_docker_services' RETURN
424+
_limit_docker_start_timeout
425+
trap '_cleanup_policy_rc_d; _remove_docker_start_timeout' RETURN
414426

415427
local repo base
416428
for base in "${repos_base[@]}"; do
@@ -430,7 +442,7 @@ install_docker_custom() {
430442
docker-buildx-plugin docker-compose-plugin; then
431443
# Unmask services and remove policy-rc.d before starting Docker ourselves
432444
_cleanup_policy_rc_d
433-
_unmask_docker_services
445+
_remove_docker_start_timeout
434446
_start_docker && return 0
435447
return 1
436448
fi
@@ -474,8 +486,8 @@ install_docker_official() {
474486
# systemctl start to fail immediately instead of blocking indefinitely
475487
# (Docker's TimeoutStartSec=0 means infinite wait on Ubuntu).
476488
_setup_policy_rc_d
477-
_mask_docker_services
478-
trap '_cleanup_policy_rc_d; _unmask_docker_services' RETURN
489+
_limit_docker_start_timeout
490+
trap '_cleanup_policy_rc_d; _remove_docker_start_timeout' RETURN
479491

480492
for mirror in "${mirrors[@]}"; do
481493
local cmd="sh get-docker.sh${mirror:+ $mirror}"
@@ -489,7 +501,7 @@ install_docker_official() {
489501
log_info "Docker installation succeeded via official script"
490502
# Unmask services and remove policy-rc.d before starting Docker ourselves
491503
_cleanup_policy_rc_d
492-
_unmask_docker_services
504+
_remove_docker_start_timeout
493505
_start_docker
494506
return $?
495507
fi

install/test_install_docker_fixes.sh

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -350,37 +350,37 @@ grep -A 30 "Debian / Ubuntu (APT)" "$INSTALL_DOCKER_SH" | grep -q "_setup_policy
350350
|| fail "install_docker.sh: custom Debian/Ubuntu path missing _setup_policy_rc_d!"
351351

352352
# ===========================================================================
353-
banner "TEST F: systemctl mask / unmask functions"
353+
banner "TEST F: limit Docker start timeout / unmask functions"
354354
# ===========================================================================
355355

356-
# Verify mask/unmask functions exist in source
357-
grep -q "^_mask_docker_services()" "$INSTALL_DOCKER_SH" \
358-
&& pass "install_docker.sh: _mask_docker_services function exists" \
359-
|| fail "install_docker.sh: _mask_docker_services missing!"
356+
# Verify timeout drop-in functions exist in source
357+
grep -q "^_limit_docker_start_timeout()" "$INSTALL_DOCKER_SH" \
358+
&& pass "install_docker.sh: _limit_docker_start_timeout function exists" \
359+
|| fail "install_docker.sh: _limit_docker_start_timeout missing!"
360360

361-
grep -q "^_unmask_docker_services()" "$INSTALL_DOCKER_SH" \
362-
&& pass "install_docker.sh: _unmask_docker_services function exists" \
363-
|| fail "install_docker.sh: _unmask_docker_services missing!"
361+
grep -q "^_remove_docker_start_timeout()" "$INSTALL_DOCKER_SH" \
362+
&& pass "install_docker.sh: _remove_docker_start_timeout function exists" \
363+
|| fail "install_docker.sh: _remove_docker_start_timeout missing!"
364364

365365
# Verify mask is used in install_docker_official
366-
grep -A 70 "^install_docker_official()" "$INSTALL_DOCKER_SH" | grep -q "_mask_docker_services" \
367-
&& pass "install_docker.sh: install_docker_official calls _mask_docker_services" \
368-
|| fail "install_docker.sh: install_docker_official missing _mask_docker_services!"
366+
grep -A 70 "^install_docker_official()" "$INSTALL_DOCKER_SH" | grep -q "_limit_docker_start_timeout" \
367+
&& pass "install_docker.sh: install_docker_official calls _limit_docker_start_timeout" \
368+
|| fail "install_docker.sh: install_docker_official missing _limit_docker_start_timeout!"
369369

370370
# Verify unmask before _start_docker in official path
371-
grep -A 80 "^install_docker_official()" "$INSTALL_DOCKER_SH" | grep -q "_unmask_docker_services" \
372-
&& pass "install_docker.sh: install_docker_official calls _unmask_docker_services before _start_docker" \
373-
|| fail "install_docker.sh: install_docker_official missing _unmask_docker_services!"
371+
grep -A 80 "^install_docker_official()" "$INSTALL_DOCKER_SH" | grep -q "_remove_docker_start_timeout" \
372+
&& pass "install_docker.sh: install_docker_official calls _remove_docker_start_timeout before _start_docker" \
373+
|| fail "install_docker.sh: install_docker_official missing _remove_docker_start_timeout!"
374374

375375
# Verify mask in custom Debian/Ubuntu path
376-
grep -A 35 "Debian / Ubuntu (APT)" "$INSTALL_DOCKER_SH" | grep -q "_mask_docker_services" \
377-
&& pass "install_docker.sh: custom Debian/Ubuntu path calls _mask_docker_services" \
378-
|| fail "install_docker.sh: custom Debian/Ubuntu path missing _mask_docker_services!"
376+
grep -A 35 "Debian / Ubuntu (APT)" "$INSTALL_DOCKER_SH" | grep -q "_limit_docker_start_timeout" \
377+
&& pass "install_docker.sh: custom Debian/Ubuntu path calls _limit_docker_start_timeout" \
378+
|| fail "install_docker.sh: custom Debian/Ubuntu path missing _limit_docker_start_timeout!"
379379

380380
# Verify unmask is called in custom Debian/Ubuntu path before _start_docker
381-
grep -A 40 "Debian / Ubuntu (APT)" "$INSTALL_DOCKER_SH" | grep -q "_unmask_docker_services" \
382-
&& pass "install_docker.sh: custom Debian/Ubuntu path calls _unmask_docker_services before _start_docker" \
383-
|| fail "install_docker.sh: custom Debian/Ubuntu path missing _unmask_docker_services!"
381+
grep -A 40 "Debian / Ubuntu (APT)" "$INSTALL_DOCKER_SH" | grep -q "_remove_docker_start_timeout" \
382+
&& pass "install_docker.sh: custom Debian/Ubuntu path calls _remove_docker_start_timeout before _start_docker" \
383+
|| fail "install_docker.sh: custom Debian/Ubuntu path missing _remove_docker_start_timeout!"
384384

385385
# ===========================================================================
386386
banner "SUMMARY"

0 commit comments

Comments
 (0)