From 6318f4b4add6d485877201f062090e3843f8ea19 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Patrick=20St=C3=B6ckle?=
Date: Sat, 24 Jan 2026 22:21:19 +0100
Subject: [PATCH] Improve container images (#749)
* :lock: chore(security): improve container image security
* :ok_hand: chore(review): add cron job to Dockerfile for periodic tasks
---
.github/workflows/release.yaml | 8 ----
db/.dockerignore | 9 +++++
db/Dockerfile | 55 ++++++++++++++++++++-----
web/.dockerignore | 9 +++++
web/Dockerfile | 74 ++++++++++++++++++++++++++--------
5 files changed, 122 insertions(+), 33 deletions(-)
create mode 100644 db/.dockerignore
create mode 100644 web/.dockerignore
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 4936e1be..5b8baa7d 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -79,17 +79,9 @@ jobs:
VERSION: ${{ needs.versioning.outputs.version }}
run: |
# Build db image
- cd db
- env GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o pocketbase_arm64
- env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o pocketbase_amd64
- cd ..
docker buildx build db/ --no-cache -t flomp/wanderer-db:$VERSION -t flomp/wanderer-db:latest --platform=linux/amd64,linux/arm64 --push
# Build web image
- export PUBLIC_VALHALLA_URL=https://valhalla1.openstreetmap.de
- cd web
- npm ci && npm run build
- cd ..
docker buildx build web/ --no-cache -t flomp/wanderer-web:$VERSION -t flomp/wanderer-web:latest --platform=linux/amd64,linux/arm64 --push
# Build docs image
diff --git a/db/.dockerignore b/db/.dockerignore
new file mode 100644
index 00000000..357e70b1
--- /dev/null
+++ b/db/.dockerignore
@@ -0,0 +1,9 @@
+*
+!commands
+!federation
+!go.*
+!integrations
+!main.go
+!migrations
+!templates
+!util
diff --git a/db/Dockerfile b/db/Dockerfile
index 8a42a650..f795a2c6 100644
--- a/db/Dockerfile
+++ b/db/Dockerfile
@@ -1,18 +1,55 @@
-FROM alpine:3.19
+FROM curlimages/curl:8.18.0 AS download-env
+
+# renovate: datasource=github-releases depName=stunnel/static-curl packageName=stunnel/static-curl
+ENV CURL_VERSION=8.18.0
+
+RUN set -eux ; \
+ ARCHITECTURE="$(uname -m)" ; \
+ case $ARCHITECTURE in \
+ x86_64) ARCHITECTURE="x86_64" ;; \
+ aarch64 | armv8* | arm64) ARCHITECTURE="aarch64" ;; \
+ *) \
+ echo "(!) Architecture $ARCHITECTURE unsupported" ; \
+ exit 1 \
+ ;; \
+ esac ; \
+ curl \
+ --connect-timeout 10 \
+ --fail \
+ --location \
+ --max-time 300 \
+ --output /tmp/curl.tar.xz \
+ --proto '=https' \
+ --show-error \
+ --silent \
+ --tlsv1.2 \
+ "https://github.com/stunnel/static-curl/releases/download/${CURL_VERSION}/curl-linux-${ARCHITECTURE}-glibc-${CURL_VERSION}.tar.xz" \
+ ; \
+ tar -xJf /tmp/curl.tar.xz -C /tmp ; \
+ chmod +x /tmp/curl ;
+
+FROM golang:1.24.0@sha256:3f7444391c51a11a039bf0359ee81cc64e663c17d787ad0e637a4de1a3f62a71 AS build
+
+WORKDIR /app
+
+COPY . .
+
+RUN go mod download
+RUN CGO_ENABLED=0 go build -o /app/pocketbase
+
+FROM scratch
WORKDIR /
+COPY --from=build /app/pocketbase /pocketbase
+COPY --from=download-env /tmp/curl /curl
COPY migrations ./migrations
COPY templates ./templates
-ARG TARGETARCH
-RUN echo ${TARGETARCH}
-COPY ./pocketbase_${TARGETARCH} /pocketbase
-RUN chmod +x /pocketbase
-
-ENV MEILI_URL=http://localhost:7700
-ENV MEILI_MASTER_KEY=
+ENV MEILI_URL=http://localhost:7700 \
+ MEILI_MASTER_KEY= \
+ POCKETBASE_ENCRYPTION_KEY=
EXPOSE 8090
-ENTRYPOINT ["/pocketbase", "serve", "--http=0.0.0.0:8090", "--dir=/pb_data"]
\ No newline at end of file
+ENTRYPOINT ["/pocketbase", "serve", "--http=0.0.0.0:8090", "--dir=/pb_data"]
diff --git a/web/.dockerignore b/web/.dockerignore
new file mode 100644
index 00000000..c8747a50
--- /dev/null
+++ b/web/.dockerignore
@@ -0,0 +1,9 @@
+*
+!*.js
+!*.ts
+!package-lock.json*
+!package.json
+!src
+!static
+!tsconfig.json
+!cron.sh
diff --git a/web/Dockerfile b/web/Dockerfile
index 150cc116..eaa67423 100644
--- a/web/Dockerfile
+++ b/web/Dockerfile
@@ -1,21 +1,63 @@
-FROM node:22-alpine
-WORKDIR /app
-COPY ./build build/
-COPY package*.json .
-RUN npm ci --omit=dev
-EXPOSE 3000
-ENV NODE_ENV=production
+FROM curlimages/curl:8.18.0 AS download-env
+# renovate: datasource=github-releases depName=stunnel/static-curl packageName=stunnel/static-curl
+ENV CURL_VERSION=8.18.0
+
+RUN set -eux ; \
+ ARCHITECTURE="$(uname -m)" ; \
+ case $ARCHITECTURE in \
+ x86_64) ARCHITECTURE="x86_64" ;; \
+ aarch64 | armv8* | arm64) ARCHITECTURE="aarch64" ;; \
+ *) \
+ echo "(!) Architecture $ARCHITECTURE unsupported" ; \
+ exit 1 \
+ ;; \
+ esac ; \
+ curl \
+ --connect-timeout 10 \
+ --fail \
+ --location \
+ --max-time 300 \
+ --output /tmp/curl.tar.xz \
+ --proto '=https' \
+ --show-error \
+ --silent \
+ --tlsv1.2 \
+ "https://github.com/stunnel/static-curl/releases/download/${CURL_VERSION}/curl-linux-${ARCHITECTURE}-glibc-${CURL_VERSION}.tar.xz" \
+ ; \
+ tar -xJf /tmp/curl.tar.xz -C /tmp ; \
+ chmod +x /tmp/curl ;
+
+FROM node:22-alpine AS build-env
+
+WORKDIR /app
+
+COPY ["package.json", "package-lock.json*", "./"]
+
+RUN npm ci
+
+COPY . .
+
+RUN npm run build
+
+FROM node:22-alpine
+
+WORKDIR /app/uploads
+WORKDIR /app
+
+COPY --from=download-env /tmp/curl /curl
+COPY --from=build-env /app /app
COPY ./cron.sh /etc/periodic/15min/cron
RUN chmod +x /etc/periodic/15min/cron
-RUN mkdir /app/uploads
-RUN apk add curl
-ENV PUBLIC_POCKETBASE_URL=http://127.0.0.1:8090
-ENV MEILI_URL=http://127.0.0.1:7700
-ENV PUBLIC_DISABLE_SIGNUP=false
-ENV UPLOAD_USER=
-ENV UPLOAD_PASSWORD=
-ENV PUBLIC_OVERPASS_API_URL=
+CMD crond && node build
-CMD crond && node build
\ No newline at end of file
+EXPOSE 3000
+
+ENV \
+ MEILI_URL=http://127.0.0.1:7700 \
+ PUBLIC_DISABLE_SIGNUP=false \
+ PUBLIC_OVERPASS_API_URL= \
+ PUBLIC_POCKETBASE_URL=http://127.0.0.1:8090 \
+ UPLOAD_PASSWORD= \
+ UPLOAD_USER=