Docs / docs (push) Successful in 24s
Playwright Tests / test-playwright (1, 2) (push) Failing after 2m28s
Playwright Tests / test-playwright (2, 2) (push) Failing after 21s
pre-commit / pre-commit (push) Failing after 2m17s
Test Backend / test-backend (3.12) (push) Successful in 4m44s
Test Backend / test-backend (3.14) (push) Successful in 4m34s
Compose Smoke Test / test-compose (push) Failing after 25s
Playwright Tests / merge-reports (push) Failing after 2m21s
144 lines
6.6 KiB
Docker
144 lines
6.6 KiB
Docker
# The whole instance in one image: engine, API and dashboard on port 8000.
|
|
#
|
|
# docker build -f backend/Dockerfile -t fluksio .
|
|
# docker run -d -p 8080:8000 -v fluksio-data:/data fluksio
|
|
#
|
|
# The build context is the app repository root. EXTRAS picks the optional
|
|
# features (mqtt, influx, email, sentry, ml, or server for all of them); the
|
|
# default is the minimal engine.
|
|
|
|
# ── Dashboard ────────────────────────────────────────────────────
|
|
# Built on the build machine's own CPU: the output is the same for every
|
|
# platform, and under emulation for an arm64 image this stage alone takes
|
|
# half an hour.
|
|
FROM --platform=$BUILDPLATFORM oven/bun:1 AS frontend
|
|
|
|
WORKDIR /app
|
|
COPY package.json bun.lock /app/
|
|
COPY frontend/package.json /app/frontend/
|
|
WORKDIR /app/frontend
|
|
RUN bun install
|
|
|
|
COPY ./frontend /app/frontend
|
|
# Empty: the dashboard calls the API as a path on whichever origin served it,
|
|
# so one build works behind any port, hostname or proxy.
|
|
ENV VITE_API_URL=
|
|
# Set only for the copy the portal serves (the appshell stage below).
|
|
ARG VITE_BASE
|
|
RUN bun run build
|
|
|
|
# ── Python environment ───────────────────────────────────────────
|
|
FROM python:3.13-slim AS backend
|
|
|
|
# Ref: https://docs.astral.sh/uv/guides/integration/docker/
|
|
COPY --from=ghcr.io/astral-sh/uv:0.13.6 /uv /bin/
|
|
# A slow link, such as a Raspberry Pi on WiFi, outlasts uv's default 30 s
|
|
# download timeout.
|
|
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy UV_HTTP_TIMEOUT=120
|
|
|
|
WORKDIR /app/
|
|
|
|
ARG EXTRAS=""
|
|
|
|
# Dependencies first, so a source change does not reinstall them.
|
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
--mount=type=bind,source=uv.lock,target=uv.lock \
|
|
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
|
uv sync --frozen --no-dev --no-install-workspace --package fluksio \
|
|
$(for e in $(echo "$EXTRAS" | tr , ' '); do printf -- '--extra %s ' "$e"; done)
|
|
|
|
COPY ./backend/scripts /app/backend/scripts
|
|
|
|
# README.md and LICENSE come along because pyproject.toml names them in
|
|
# `readme` and `license-files`; hatchling reads both when it builds the
|
|
# package metadata below, and fails the build if either is missing.
|
|
COPY ./backend/pyproject.toml ./backend/alembic.ini ./backend/README.md ./backend/LICENSE /app/backend/
|
|
|
|
COPY ./backend/fluksio /app/backend/fluksio
|
|
|
|
# The worker is a workspace member of its own, so the engine's sync needs it
|
|
# present to resolve the dependency on it. It is also what the engine runs
|
|
# node code with.
|
|
COPY ./worker /app/worker
|
|
|
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
--mount=type=bind,source=uv.lock,target=uv.lock \
|
|
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
|
uv sync --frozen --no-dev --package fluksio \
|
|
$(for e in $(echo "$EXTRAS" | tr , ' '); do printf -- '--extra %s ' "$e"; done)
|
|
|
|
# Connectors are ordinary installed packages found through the
|
|
# `fluksio.node_types` entry point. Place built connector wheels here;
|
|
# an image with none is the normal case.
|
|
COPY ./backend/connector-wheels /tmp/connector-wheels
|
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
sh -c 'ls /tmp/connector-wheels/*.whl >/dev/null 2>&1 \
|
|
&& uv pip install --python /app/.venv/bin/python /tmp/connector-wheels/*.whl \
|
|
|| echo "no connectors bundled"'
|
|
|
|
# ── The portal's copy of the dashboard ──────────────────────────
|
|
# The hub serves the dashboard for every instance it tunnels to, from
|
|
# /app-shell/. index/docker/compose.yml builds this target with
|
|
# VITE_BASE=/app-shell/ and copies /dist into the hub's volume.
|
|
FROM busybox:1.37 AS appshell
|
|
COPY --from=frontend /app/frontend/dist /dist
|
|
|
|
# ── git, without perl ────────────────────────────────────────────
|
|
# The flow store runs init, add, commit, gc and rev-parse, all built into the
|
|
# one binary, which needs nothing slim lacks. Debian's package would bring
|
|
# perl and the remote helpers along, about 100 MB nothing here calls.
|
|
FROM python:3.13-slim AS git
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends git \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# ── Runtime ──────────────────────────────────────────────────────
|
|
FROM python:3.13-slim
|
|
|
|
COPY --from=git /usr/bin/git /usr/bin/git
|
|
|
|
# redis-server keeps message state when no external Redis is named. tini reaps
|
|
# the python node workers that outlive their pool, which would otherwise pile
|
|
# up as zombies under PID 1. procps is uptime, ps and free for Command nodes,
|
|
# the demo's among them. The git round trip fails the build if the binary
|
|
# above ever needs something this image does not have.
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends procps redis-server tini \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& git init -q /tmp/g 2>/dev/null \
|
|
&& git -C /tmp/g -c user.name=x -c user.email=x@x commit -q --allow-empty -m x \
|
|
&& git -C /tmp/g gc --quiet && git -C /tmp/g rev-parse HEAD >/dev/null \
|
|
&& rm -rf /tmp/g
|
|
|
|
# /app/.venv holds the app and nothing of anybody else's, so there is nothing
|
|
# here to adopt: node code gets a venv of its own on the data volume, which is
|
|
# what the Modules screen installs into.
|
|
ENV PYTHONUNBUFFERED=1 \
|
|
PATH="/app/.venv/bin:$PATH" \
|
|
NODE_VENV=managed \
|
|
FRONTEND_DIST=/app/frontend/dist \
|
|
FRONTEND_HOST=http://localhost:8000
|
|
|
|
# The venv installs the app editable, so the source stays at the path it was
|
|
# synced from. uv comes with it: the Modules screen installs with it.
|
|
COPY --from=backend /app /app
|
|
COPY --from=frontend /app/frontend/dist /app/frontend/dist
|
|
COPY ./backend/docker-entrypoint.sh /usr/local/bin/
|
|
|
|
WORKDIR /app/backend/
|
|
VOLUME /data
|
|
EXPOSE 8000
|
|
|
|
# Deep health: fails when the event loop is wedged or Redis is gone, not just
|
|
# when the process is dead.
|
|
HEALTHCHECK --interval=10s --timeout=5s --start-period=30s --retries=5 \
|
|
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/utils/health/', timeout=4)"
|
|
|
|
ENTRYPOINT ["tini", "--", "docker-entrypoint.sh"]
|
|
|
|
# Single process on purpose: it hosts the flow engine, and a second worker
|
|
# would be a second engine — duplicated subscriptions, cron ticks and webhooks.
|
|
# `serve` makes the secret key and the first admin on first start, and prints
|
|
# the admin's password once.
|
|
CMD ["fluksio", "serve", "--plain", "--host", "0.0.0.0", "--port", "8000", "--data-dir", "/data"]
|