Repository navigation
Expand file tree
/
Copy pathDockerfile
More file actions
173 lines (132 loc) · 5.99 KB
/
Copy pathDockerfile
File metadata and controls
173 lines (132 loc) · 5.99 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
163
164
165
166
167
168
169
170
171
172
173
# Stage 1a: Dash0 Build
#
# Pinned to --platform=$BUILDPLATFORM: this stage only produces static
# assets (no native code), so it must run natively on the build host rather
# than under QEMU emulation for the target platform (see backend-builder
# below for the stage that actually varies per target).
FROM --platform=$BUILDPLATFORM node:24-alpine AS dash0-builder
# Install bun
RUN apk add --no-cache curl unzip bash && \
curl -fsSL https://bun.sh/install | bash && \
ln -s /root/.bun/bin/bun /usr/local/bin/bun
WORKDIR /build/dash0
# Copy dash0 package files
COPY web/dash0/package.json web/dash0/bun.lock ./
# Install dependencies
RUN bun install --frozen-lockfile
# Copy dash0 source
COPY web/dash0/ ./
# Build dash0
RUN bun run build
# Stage 1b: Status0 Build (static assets — see dash0-builder above)
FROM --platform=$BUILDPLATFORM node:24-alpine AS status0-builder
# Install bun
RUN apk add --no-cache curl unzip bash && \
curl -fsSL https://bun.sh/install | bash && \
ln -s /root/.bun/bin/bun /usr/local/bin/bun
WORKDIR /build/status0
# Copy status0 package files
COPY web/status0/package.json web/status0/bun.lock ./
# Install dependencies
RUN bun install --frozen-lockfile
# Copy status0 source
COPY web/status0/ ./
# Build status0
RUN bun run build
# Stage 1c: Docs Build (Docusaurus, incl. generated API reference — static assets, see dash0-builder above)
FROM --platform=$BUILDPLATFORM node:24-alpine AS docs-builder
# Install bun
RUN apk add --no-cache curl unzip bash && \
curl -fsSL https://bun.sh/install | bash && \
ln -s /root/.bun/bin/bun /usr/local/bin/bun
WORKDIR /build/web/docs
# Copy docs package files
COPY web/docs/package.json web/docs/bun.lock ./
# Install dependencies
RUN bun install --frozen-lockfile
# The API reference is generated at build time from the canonical OpenAPI spec
# via the relative path ../../server/internal/app/openapi/openapi.yaml — make it
# available at that location in this stage.
COPY server/internal/app/openapi/openapi.yaml /build/server/internal/app/openapi/openapi.yaml
# The changelog page is generated at build time from the root CHANGELOG.md via
# the relative path ../../../CHANGELOG.md (see scripts/gen-changelog.ts) — make
# it available at that location in this stage.
COPY CHANGELOG.md /build/CHANGELOG.md
# Copy docs source
COPY web/docs/ ./
# Build docs (runs gen-api-docs then docusaurus build)
RUN bun run build
# Stage 2: Backend Build
#
# Pinned to --platform=$BUILDPLATFORM: the Go toolchain itself always runs
# natively on the build host, and cross-compiles the OUTPUT binary for
# TARGETOS/TARGETARCH via the CGO_ENABLED=0 build below (no QEMU emulation
# needed for the compiler, unlike a CGO build would require).
#
# The dependency layer is its own stage so CI can build it on every PR
# (`docker build --target backend-deps .`); the image itself is only built on
# tags. Every local `replace` target in server/go.mod needs its go.mod/go.sum
# copied here, or `go mod download` fails before the source is copied in.
FROM --platform=$BUILDPLATFORM golang:1.27.1-trixie AS backend-deps
WORKDIR /build
COPY server/go.mod server/go.sum ./server/
COPY server/third_party/grdp/go.mod server/third_party/grdp/go.sum ./server/third_party/grdp/
WORKDIR /build/server
RUN go mod download
FROM backend-deps AS backend-builder
# Build arguments for version information
ARG VERSION=dev
ARG COMMIT=unknown
ARG GIT_TIME=unknown
# Set by buildx to the platform requested via `--platform` on the final
# image (e.g. "linux" / "arm64"), independent of the build host.
ARG TARGETOS
ARG TARGETARCH
# Copy backend source
COPY server/ ./
# Copy SPA build artifacts to embed locations
COPY --from=dash0-builder /build/dash0/dist ./internal/app/dash0res
COPY --from=status0-builder /build/status0/dist ./internal/app/status0res
COPY --from=docs-builder /build/web/docs/build ./internal/app/docsres
# Build the backend binary with version information. CGO is NOT needed: the
# shipped SQLite driver is pure-Go modernc
# (internal/db/sqlitedriver/sqlitedriver.go), so this cross-compiles cleanly
# for GOOS/GOARCH without a C toolchain or QEMU emulation — the same way the
# release binaries (ci.yml) and the sp CLI image already build.
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build \
-ldflags "\
-X 'github.com/fclairamb/solidping/server/internal/version.Version=${VERSION}' \
-X 'github.com/fclairamb/solidping/server/internal/version.Commit=${COMMIT}' \
-X 'github.com/fclairamb/solidping/server/internal/version.GitTime=${GIT_TIME}'" \
-o /solidping .
# The final stage is distroless and has no shell, so /data/files can't be
# created there. Create it here and copy it across with the right ownership
# (see the final stage) so a fresh named volume seeds correctly.
RUN mkdir -p /data/files
# Stage 3: Final Runtime Image
FROM gcr.io/distroless/base-debian13:nonroot
WORKDIR /app
# Copy the compiled binary
COPY --from=backend-builder /solidping /app/solidping
# Seed /data (database + uploads) owned by the nonroot user (65532:65532) so
# a fresh named volume, which Docker populates from the image directory's
# content and ownership, is writable on first run.
COPY --from=backend-builder --chown=65532:65532 /data /data
# The image is self-contained by default: SQLite database and uploads live
# under /data, so `docker run -v solidping-data:/data ...` is enough. Every
# value can still be overridden with -e (SP_DB_TYPE=postgres + SP_DB_URL for
# Postgres, for example).
ENV SP_DB_TYPE=sqlite \
SP_DB_DIR=/data \
SP_FILESTORAGE_LOCAL_ROOT=/data/files
VOLUME /data
# Expose default port
EXPOSE 4000
# The image has no shell/curl (distroless), so the probe is the binary
# itself calling its own /api/mgmt/health over loopback
# (internal/healthcheck). 503 during the graceful-shutdown window makes the
# container report unhealthy before it stops, which is the wanted signal.
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD ["/app/solidping", "healthcheck"]
# Set entrypoint
ENTRYPOINT ["/app/solidping", "serve"]