Repository navigation
Expand file tree
/
Copy pathDockerfile.runtime-base
More file actions
162 lines (148 loc) · 7.09 KB
/
Copy pathDockerfile.runtime-base
File metadata and controls
162 lines (148 loc) · 7.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# syntax=docker/dockerfile:1
#
# Shared Borg runtime base image with Borg 1.x and Borg 2.x preinstalled.
# It exists so that every component that needs borg1/borg2 can build FROM it
# instead of compiling Borg itself — Borg is compiled once here, not rebuilt for
# every app tag.
#
# Two-stage build: the compile toolchain (build-essential + *-dev headers,
# ~350-400 MB) lives only in the `builder` stage. The final `runtime-base`
# stage ships just the shared libraries Borg links against plus the operational
# tooling, so the toolchain never reaches the published image.
# The Python the image is built on — kept in step with runtime-base.env by a
# guard test. The FROM lines and the site-packages COPY paths derive from it.
ARG PYTHON_VERSION=3.12
# --- builder: compile the Borg wheels (needs the toolchain + -dev headers) ----
FROM python:${PYTHON_VERSION}-slim AS builder
ARG BORG1_VERSION=1.4.5
ARG BORG2_VERSION=2.0.0b21
ARG BORGSTORE_VERSION=0.4.1
# Build-time only. OS packages track the base image's Debian release rather than
# pinned versions (DL3008): upstream does not pin them either and a pin would
# break on the next base bump. None of this reaches the runtime stage.
# hadolint ignore=DL3008
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
python3-dev \
pkg-config \
libacl1-dev \
libssl-dev \
liblz4-dev \
libzstd-dev \
libxxhash-dev \
libfuse3-dev \
ca-certificates \
curl \
unzip \
&& rm -rf /var/lib/apt/lists/*
# Borg 1.x into the image Python, Borg 2.x into its own venv. Borg versions are
# pinned via ARG; pyfuse3 tracks the interpreter and is intentionally unpinned
# (DL3013). Kept as its own layer, separate from the apt layer above (DL3059),
# so a Borg version bump does not re-run the OS package install.
#
# The Borg 2 venv gets every borgstore repository backend so any repo scheme
# works out of the box: posixfs (file://) is built in and needs no extra, while
# sftp:// (paramiko), rclone:// (requests), rest:// (requests) and s3:// (boto3)
# are pulled via the explicit extras below. All are listed even though some
# share deps (rclone+rest both only need requests), so it is obvious at a glance
# that no scheme is missing. NB: posixfs is NOT a pip extra — it is always
# available — so it must not appear in the brackets.
# hadolint ignore=DL3013,DL3059
RUN pip install --no-cache-dir pyfuse3 "borgbackup==${BORG1_VERSION}" && \
python3 -m venv /opt/borg2-venv && \
/opt/borg2-venv/bin/pip install --no-cache-dir pyfuse3 "borgbackup==${BORG2_VERSION}" "borgstore[rclone,sftp,rest,s3]==${BORGSTORE_VERSION}"
# rclone: fetch the official static binary from downloads.rclone.org rather than
# the Debian package. The distro package lags upstream by years and ships as a
# "-DEV" build (bookworm carries v1.60.1-DEV), predating the OneDrive
# chunked-upload auth fix (rclone v1.65.1) — so cloud-mirror sync to OneDrive
# fails with "unauthenticated" on every file. Version single-sourced from
# runtime-base.env; the download+unzip tooling stays in this builder stage and
# only the verified binary is copied into the runtime image. Bump RCLONE_VERSION
# and both checksums together to upgrade.
ARG RCLONE_VERSION=1.75.0
ARG RCLONE_SHA256_AMD64=aa2804e08f48250e71009c727124b6341cd0288465804a9a09d14663cabafbaa
ARG RCLONE_SHA256_ARM64=d0ad88ba4c8e285b7c9efa591e0ab643280a91741e13c27f3a9c0957ccfa5203
# TARGETARCH is provided automatically by BuildKit for each target platform.
ARG TARGETARCH
RUN set -eux; \
case "${TARGETARCH}" in \
amd64) rclone_sha="${RCLONE_SHA256_AMD64}" ;; \
arm64) rclone_sha="${RCLONE_SHA256_ARM64}" ;; \
*) echo "unsupported TARGETARCH for rclone: ${TARGETARCH}" >&2; exit 1 ;; \
esac; \
zip="rclone-v${RCLONE_VERSION}-linux-${TARGETARCH}.zip"; \
curl -fsSL -o /tmp/rclone.zip "https://downloads.rclone.org/v${RCLONE_VERSION}/${zip}"; \
printf '%s /tmp/rclone.zip\n' "${rclone_sha}" > /tmp/rclone.sha256; \
sha256sum -c /tmp/rclone.sha256; \
unzip -j -o /tmp/rclone.zip "*/rclone" -d /out; \
chmod 0755 /out/rclone; \
# rclone reads RCLONE_* env vars as flags, and BuildKit exposes these ARGs
# as env vars in RUN, so RCLONE_VERSION collides with the --version flag —
# clear them for the verification call (the final runtime image sets none).
env -u RCLONE_VERSION -u RCLONE_SHA256_AMD64 -u RCLONE_SHA256_ARM64 /out/rclone version; \
rm -f /tmp/rclone.zip /tmp/rclone.sha256
# --- runtime-base: slim image, no compilers — runtime libs + operational tools -
FROM python:${PYTHON_VERSION}-slim AS runtime-base
ARG PYTHON_VERSION
ARG BORG1_VERSION=1.4.5
ARG BORG2_VERSION=2.0.0b21
ENV BORG1_VERSION=${BORG1_VERSION}
ENV BORG2_VERSION=${BORG2_VERSION}
LABEL org.opencontainers.image.title="Borg UI Runtime Base"
LABEL org.opencontainers.image.description="Shared runtime base for Borg UI with Borg 1.x and Borg 2.x preinstalled"
LABEL org.opencontainers.image.vendor="Borg UI"
LABEL org.opencontainers.image.source="https://github.com/karanhudia/borg-ui"
WORKDIR /app
# Runtime shared libraries Borg links against (the runtime counterparts of the
# builder's -dev headers) plus the operational tooling. No compilers here.
# OS packages track the base Debian release rather than pinned versions (DL3008).
# hadolint ignore=DL3008
RUN apt-get update && apt-get install -y --no-install-recommends \
cron \
curl \
wget \
gnupg \
gosu \
sudo \
libacl1 \
liblz4-1 \
libzstd1 \
libxxhash0 \
libssl3t64 \
libfuse3-4 \
fuse3 \
rsync \
btrfs-progs \
openssh-client \
sshfs \
htop \
iotop \
net-tools \
iputils-ping \
tree \
ncdu \
sshpass \
&& rm -rf /var/lib/apt/lists/*
# Borg was compiled in the builder stage — copy the artifacts, not the toolchain.
COPY --from=builder /usr/local/lib/python${PYTHON_VERSION}/site-packages /usr/local/lib/python${PYTHON_VERSION}/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
COPY --from=builder /opt/borg2-venv /opt/borg2-venv
# rclone was fetched and checksum-verified in the builder stage — copy just the
# binary (see the builder for why the distro package is unusable).
COPY --from=builder /out/rclone /usr/bin/rclone
# Symlink borg2, create the runtime directories, and set up the borg user.
RUN ln -sf /opt/borg2-venv/bin/borg /usr/local/bin/borg2 && \
mkdir -p \
/data /data/ssh_keys /data/borg_keys /data/logs /data/config \
/backups /var/log/borg /etc/borg && \
groupadd -g 1001 borg && \
useradd -m -u 1001 -g 1001 -s /bin/bash borg && \
usermod -a -G sudo borg && \
groupadd -f fuse && \
usermod -a -G fuse borg && \
echo "borg ALL=(ALL) NOPASSWD: /usr/bin/borg, /usr/bin/crontab, /usr/bin/apt-get" >> /etc/sudoers && \
sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf && \
mkdir -p /home/borg/.ssh /home/borg/.cache/borg /etc/cron.d && \
chown -R borg:borg /app /data /backups /var/log/borg /etc/borg /home/borg/.ssh /home/borg/.cache /etc/cron.d && \
chmod -R 755 /app /data /backups /var/log/borg /etc/borg && \
chmod 700 /home/borg/.ssh /home/borg/.cache/borg