feat: Add docker image build and publish pipeline - #109
Conversation
The webui has no published Docker image today. Every downstream deployment template (compose stack, Helm, cloud providers) needs ghcr.io/trakli/webui to exist. Dockerfile (multi-stage): - Stage 1 (node:lts): npm ci + npm run build with a placeholder API URL baked into the output. Nuxt 3 runtimeConfig.public values are embedded at build time, so we cannot set the real URL here. - Stage 2 (node:lts-alpine): copies only .output/ from the build stage. Alpine-based for a small final image. docker/entrypoint.sh: - At container start, sed-replaces the build-time placeholder (http://__NUXT_PUBLIC_API_BASE_URL__) in all .output/ JS files with the actual NUXT_PUBLIC_API_BASE_URL env var, then starts the Node server. Standard Nuxt 3 pattern for a single image across deployments. .github/workflows/build-and-publish.yml: - Mirrors the webservice workflow: SHA tag on dev push, latest + release semver tag on GitHub Release publish. Pushes to ghcr.io. .dockerignore: - Excludes node_modules, .nuxt, .output, .git, .env*, tests. Issue: trakli/webservice#308
Code Review SummaryThis PR successfully introduces a Dockerized deployment path for the Trakli WebUI. It includes a multi-stage Dockerfile optimized for production and a CI/CD pipeline for automated publishing to GHCR. 🚀 Key Improvements
💡 Minor Suggestions
|
| WORKDIR /app | ||
|
|
There was a problem hiding this comment.
For security best practices, the container should run as a non-root user. The node-alpine image provides a node user (UID 1000) that can be utilized. Ensure the files are owned by this user so that the entrypoint script (which uses sed -i) has permission to modify them at runtime.
| WORKDIR /app | |
| +WORKDIR /app | |
| + | |
| +COPY --from=builder --chown=node:node /app/.output .output | |
| +COPY --from=builder --chown=node:node /app/docker/entrypoint.sh /entrypoint.sh | |
| +RUN chmod +x /entrypoint.sh | |
| + | |
| +USER node |
There was a problem hiding this comment.
Pull request overview
Adds Docker packaging and a GitHub Actions workflow so the Nuxt web UI can be built and published to GHCR as ghcr.io/trakli/webui, enabling downstream deployment templates to consume a prebuilt image.
Changes:
- Introduces a multi-stage
Dockerfilethat builds the Nuxt app and ships only.output/in a slim runtime image. - Adds a container
docker/entrypoint.shthat patches the baked Nuxt public runtime config placeholder at startup. - Adds a GitHub Actions workflow to build/push images on
devpushes and release publishes, plus a.dockerignore.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| Dockerfile | Multi-stage build and runtime image setup (healthcheck + entrypoint). |
| docker/entrypoint.sh | Startup-time placeholder replacement for Nuxt public runtime config. |
| .github/workflows/build-and-publish.yml | CI build/push pipeline to GHCR for dev commits and releases. |
| .dockerignore | Reduces Docker build context size by excluding local/dev artifacts. |
Suppressed comments (1)
Dockerfile:22
- To keep runtime behavior aligned with CI (which runs on Node.js 20), consider pinning the final image to Node 20 as well instead of
node:lts-alpine(which can jump major versions over time).
FROM node:lts-alpine
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if [ -n "$NUXT_PUBLIC_API_BASE_URL" ]; then | ||
| find /app/.output -type f \( -name '*.mjs' -o -name '*.js' -o -name '*.cjs' \) \ | ||
| -exec sed -i "s|http://__NUXT_PUBLIC_API_BASE_URL__|${NUXT_PUBLIC_API_BASE_URL}|g" {} + | ||
| fi |
| WORKDIR /app | ||
|
|
||
| COPY --from=builder /app/.output .output | ||
| COPY --from=builder /app/docker/entrypoint.sh /entrypoint.sh | ||
| RUN chmod +x /entrypoint.sh |
| # so a single image works across deployments with different API URLs. | ||
|
|
||
| # ── Stage 1: Build ────────────────────────────────────────────────────────── | ||
| FROM node:lts AS builder |
What
Adds a multi-stage Dockerfile, entrypoint script, CI workflow, and .dockerignore so the webui can be published as
ghcr.io/trakli/webui— the image every downstream deployment template needs.This is PR 2 of Issue #308 — Distribution, Helm, Cloud Templates.
Why
The webui has no published Docker image today. Self-hosting, FlatRun, Helm, and every cloud template in #308 depend on pulling this image. Without it, Phase 2–4 templates can't reference a webui image.
How
Dockerfile (multi-stage):
node:lts):npm ci+npm run buildwith a placeholder API URL baked into the outputnode:lts-alpine): copies only.output/— small final imageRuntime config fix: Nuxt 3 bakes
runtimeConfig.publicvalues into JS at build time. Build withhttp://__NUXT_PUBLIC_API_BASE_URL__as placeholder. At container start,docker/entrypoint.shsed-replaces it with the actual env var, then starts the Node server.CI workflow (
.github/workflows/build-and-publish.yml):latest+ release semver on GitHub Release publishdocker/build-push-actionwithGITHUB_TOKENauthHealthcheck:
wget -qO- http://localhost:3000/on port 3000.Checklist
docker buildsucceedsghcr.io/trakli/webuiwith correct tag scheme