Skip to content

Commit 276b881

Browse files
authored
Merge pull request #161 from getlantern/fix-do-apt-locks
fix: resolve DO apt lock race in Packer provisioning
2 parents 63b9795 + 6283fa8 commit 276b881

File tree

1 file changed

+29
-11
lines changed

1 file changed

+29
-11
lines changed

deploy/packer/provision.sh

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,37 @@ set -euo pipefail
66

77
export DEBIAN_FRONTEND=noninteractive
88

9-
# Kill unattended-upgrades and wait for any running apt/dpkg to finish.
10-
# Fresh VPS instances often have unattended-upgrades holding apt locks.
9+
# Wait for cloud-init to finish — it triggers apt operations on fresh VPS instances.
10+
if command -v cloud-init >/dev/null 2>&1; then
11+
echo "==> Waiting for cloud-init to finish (max 5 minutes)"
12+
if timeout 300s cloud-init status --wait; then
13+
echo " cloud-init finished successfully"
14+
else
15+
echo " WARNING: cloud-init wait exited with code $?" >&2
16+
fi
17+
fi
18+
19+
# Stop unattended-upgrades so it doesn't race with our apt-get calls.
20+
# We mask it during provisioning but re-enable at the end of the script
21+
# so the final image still receives automatic security updates.
1122
echo "==> Stopping unattended-upgrades"
1223
systemctl stop unattended-upgrades.service 2>/dev/null || true
24+
systemctl mask unattended-upgrades.service 2>/dev/null || true
1325
systemctl kill --signal=TERM apt-daily.service 2>/dev/null || true
1426
systemctl kill --signal=TERM apt-daily-upgrade.service 2>/dev/null || true
27+
# Kill any lingering apt/unattended-upgrade processes (but not dpkg — killing
28+
# dpkg mid-transaction can corrupt the package database).
29+
killall -9 apt-get unattended-upgrade 2>/dev/null || true
30+
sleep 2
1531

16-
# Wait for any lingering dpkg/apt processes to exit
17-
while fuser /var/lib/dpkg/lock-frontend /var/lib/apt/lists/lock /var/cache/apt/archives/lock >/dev/null 2>&1; do
18-
echo " Waiting for apt/dpkg locks..."
19-
sleep 3
20-
done
32+
# Use apt-get's built-in lock timeout (wait up to 5 minutes for locks to clear)
33+
# instead of a fragile fuser loop that can race between update and install.
34+
APT_OPTS=(-o DPkg::Lock::Timeout=300)
2135

2236
echo "==> Installing runtime dependencies"
23-
apt-get update -q
37+
apt-get "${APT_OPTS[@]}" update -q
2438
# Keep this package list in sync with Dockerfile
25-
apt-get install -y -q \
39+
apt-get "${APT_OPTS[@]}" install -y -q \
2640
ca-certificates \
2741
tzdata \
2842
nftables
@@ -35,7 +49,7 @@ echo " URL: ${deb_url}"
3549
curl -fsSL -o "/tmp/${deb_name}" "${deb_url}"
3650

3751
echo "==> Installing ${deb_name}"
38-
dpkg -i "/tmp/${deb_name}"
52+
apt-get "${APT_OPTS[@]}" install -y -q "/tmp/${deb_name}"
3953
rm -f "/tmp/${deb_name}"
4054

4155
# The .deb installs the binary as /usr/bin/sing-box-extensions.
@@ -67,7 +81,7 @@ otelcol_deb="otelcol-contrib_${otelcol_version}_linux_${arch}.deb"
6781
otelcol_url="https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${otelcol_version}/${otelcol_deb}"
6882
echo " URL: ${otelcol_url}"
6983
curl -fsSL -o "/tmp/${otelcol_deb}" "${otelcol_url}"
70-
dpkg -i "/tmp/${otelcol_deb}"
84+
apt-get "${APT_OPTS[@]}" install -y -q "/tmp/${otelcol_deb}"
7185
rm -f "/tmp/${otelcol_deb}"
7286

7387
# Copy our config into the otelcol-contrib config directory.
@@ -88,6 +102,10 @@ DROPIN
88102
systemctl daemon-reload
89103
# Do NOT enable — cloud-init writes env vars first, then enables the service.
90104

105+
# Re-enable unattended-upgrades so the final image receives security updates.
106+
systemctl unmask unattended-upgrades.service 2>/dev/null || true
107+
systemctl enable unattended-upgrades.service 2>/dev/null || true
108+
91109
echo "==> Verifying installation"
92110
if ! command -v lantern-box >/dev/null 2>&1; then
93111
echo "lantern-box not found on PATH" >&2

0 commit comments

Comments
 (0)