# The Python CI tests on (ci-python:3.14), so the image runs what was tested. FROM python:3.14-slim RUN DEBIAN_FRONTEND=noninteractive apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ iputils-ping \ # gosu — minimal privilege-drop tool used by entrypoint.sh gosu \ # openssh-client — ansible-playbook reaches remote hosts over ssh openssh-client \ # sshpass — Ansible shells out to it for first-contact password SSH # (provisioning a fresh host before the managed key is installed) sshpass \ # git — clones and pulls Ansible git sources (Settings > Ansible) git \ # tini — PID 1: reaps the zombies ssh and ansible leave behind and # forwards SIGTERM to Steward so a stop is a graceful shutdown tini \ # tzdata — the zone database, so times show in each reader's own zone # (milestone 490); Debian's slim base no longer carries it tzdata \ # postgresql-client-17 — pg_dump and pg_restore for Steward's own # backups (milestone 507); 17 reads and writes a Postgres 16 server postgresql-client-17 \ && rm -rf /var/lib/apt/lists/* # Upgrade pip to suppress the version notice RUN pip install --upgrade pip WORKDIR /app COPY pyproject.toml . COPY steward/ steward/ # Bundle the extras whose features ship in the image: [ansible] for the # playbook runner, [snmp] (pysnmp) for the SNMP poller, [ldap] (ldap3, pure # Python) for LDAP sign-in under Settings -> Auth. Without them those features # load but fail for want of their runtime dependency. RUN pip install --no-cache-dir '.[ansible,snmp,ldap]' COPY alembic.ini . # First-party plugins ship inside the image (bundled root at /app/plugins). # Third-party plugins are mounted at runtime into the external root # (STEWARD_PLUGIN_DIR, default /data/plugins). COPY plugins/ plugins/ COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh # Runtime directories — owned by app user. The container starts as root # so the entrypoint can fix up /data permissions if needed, then drops to # 'app' (uid 1000) via gosu before launching steward. RUN useradd -m -u 1000 app \ && mkdir -p /data/ansible/sources /data/ansible/runs /data/ansible/playbooks /data/plugins \ && chown -R app:app /data /app EXPOSE 5000 5514/udp 5514/tcp ENTRYPOINT ["/usr/bin/tini", "--", "/entrypoint.sh"] CMD ["steward", "--host", "0.0.0.0", "--port", "5000"]