vm-selector/Dockerfile
Antoine Pelletier fb47979787
All checks were successful
build / image (push) Successful in 1m15s
fix: image uid
2026-07-30 00:14:55 +02:00

83 lines
3.2 KiB
Docker

# Frontend: built first, it is the part that changes most often
FROM node:22-slim AS frontend
WORKDIR /build
# Dependencies in their own layer, so a source-only change does not reinstall them
COPY frontend/package.json frontend/package-lock.json ./
RUN npm ci
COPY frontend/ ./
# The checks run here rather than in a separate CI job: the build then fails on
# the runner for the same reason it would fail locally, and there is only one
# environment to keep working.
RUN npx eslint . --max-warnings=0
RUN npm run build # type-check + vite build
# Backend: compiled against the sqlx offline data, so no database is needed here
FROM rust:1.97-slim AS backend
WORKDIR /build
ENV SQLX_OFFLINE=true
# Traceability: `build.rs` falls back on this when there is no git repository
ARG GIT_HASH=unknown
ENV GIT_HASH=${GIT_HASH}
# Warm the dependency layer with a dummy main, so editing src/ does not rebuild
# every crate we depend on
COPY Cargo.toml Cargo.lock build.rs ./
RUN mkdir src && echo 'fn main() {}' > src/main.rs \
&& cargo build --release \
&& rm -rf src
COPY .sqlx/ .sqlx/
COPY src/ src/
# cargo skips a rebuild when only mtime changed: force it for our own crate
RUN touch src/main.rs \
&& cargo build --release --locked \
&& cargo test --release --locked
# Runtime
FROM debian:trixie-slim
LABEL org.opencontainers.image.title="vm-selector"
# virsh is how the app talks to libvirt. openssh-client is only needed if you
# point vm.uri at a qemu+ssh:// uri instead of the mounted socket; curl serves
# the healthcheck.
RUN apt-get update \
&& apt-get install -y --no-install-recommends libvirt-clients openssh-client curl \
&& rm -rf /var/lib/apt/lists/*
# The migrations travel with the image, so the stack needs nothing from the
# repository: the `migrate` service runs this same image with another entrypoint.
COPY --from=ghcr.io/amacneil/dbmate:2 /usr/local/bin/dbmate /usr/local/bin/dbmate
COPY db/migrations/ /app/db/migrations/
# debian:*-slim ships without /etc/nsswitch.conf, and glibc then cannot turn a
# uid back into a user record. Restore the plain file-based lookup.
RUN printf 'passwd: files\ngroup: files\nshadow: files\nhosts: files dns\n' \
> /etc/nsswitch.conf
# Runs as root, on purpose. libvirt resolves the calling uid to a user record
# before it even opens the socket, and refused to do so for a custom uid here
# ("Failed to find user record for uid") even with /etc/passwd and the libvirt
# group both in place. Root always resolves.
#
# The privilege actually gained is small: any process that can reach the libvirt
# socket already starts and destroys the VMs of this host, which is the whole
# point of this container. It holds no other mount and no secret beyond the
# database password.
WORKDIR /app
COPY --from=backend /build/target/release/app-template /usr/local/bin/vm-selector
COPY --from=frontend /build/dist/ /app/frontend/
COPY docker/config.yml /etc/app-template/config.yml
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -fsS http://localhost:3000/api/version || exit 1
# No shell: signals reach the binary directly, so the graceful shutdown works
ENTRYPOINT ["/usr/local/bin/vm-selector"]