# ============================================================================ # Open Terminal — Slim Image (Debian) # ============================================================================ # # A minimal, hardened image for running the Open Terminal API. # No Node.js, no Docker CLI, no data-science libraries, no sudo. # Just the terminal API, git, curl, and the egress firewall. # # Build: # docker build -f Dockerfile.slim -t open-terminal:slim . # # Run: # docker run -d -p 8000:8000 -e OPEN_TERMINAL_API_KEY=secret open-terminal:slim # # Customize: # - Want extra apt packages? Add them to the "Runtime packages" section below. # - Want extra pip packages? Add them to the "pip install" line in the builder. # - Need Node.js or heavier tools? Use the full image (Dockerfile) instead. # # ============================================================================ # -------------------------------------------------------------------------- # Stage 1: Builder # Compiles Python dependencies in an isolated prefix so we can copy only # the final result into the slim runtime image — no compilers shipped. # -------------------------------------------------------------------------- FROM python:3.12.13-slim AS builder WORKDIR /src # Build-time system dependencies. # If a pip package needs extra C libraries to compile, add the -dev # package here (e.g. "libpq-dev" for psycopg2). RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ gcc \ libffi-dev \ && rm -rf /var/lib/apt/lists/* COPY pyproject.toml README.md uv.lock* ./ COPY open_terminal/ open_terminal/ # Install the app + dependencies into /install so we can cherry-pick them. # ➡️ To add extra pip packages to the slim image, append them here: # e.g. pip install --no-cache-dir --prefix=/install . httpx polars RUN pip install --no-cache-dir --prefix=/install . # -------------------------------------------------------------------------- # Stage 2: Runtime # The actual image that ships. No compilers, no build tools, no sudo. # -------------------------------------------------------------------------- FROM python:3.12.13-slim # ── Runtime system packages ──────────────────────────────────────────────── # # These are the only apt packages in the final image. The list is kept # intentionally tiny. If you need something extra, just add it below. # # Core: tini (PID 1), gosu (drop privileges) # Utilities: curl, git, jq, less, procps (ps/top) # Firewall: iptables, ipset, dnsmasq (egress whitelist) # # ➡️ Want more tools? Add them here, one per line for easy diffs: # e.g. vim \ # sqlite3 \ # RUN apt-get update && apt-get install -y --no-install-recommends \ tini \ gosu \ curl \ git \ jq \ less \ procps \ iptables \ ipset \ dnsmasq \ libcap2-bin \ ca-certificates \ && rm -rf /var/lib/apt/lists/* # Copy the pre-built Python packages from the builder stage. COPY --from=builder /install /usr/local WORKDIR /app COPY . . # Uncomment to apply security patches beyond what the base image provides. # Not recommended for reproducible builds; prefer bumping the base image tag. # RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/* # Install the app itself (fast — all deps are already present). # # Cleanup removes ~20 MB of unnecessary files: # - pip itself (not needed at runtime) # - __pycache__ / .pyc / .pyo bytecode (regenerated on first import) # - test directories inside packages # RUN pip install --no-cache-dir --no-deps . \ && pip cache purge 2>/dev/null || true \ && pip uninstall -y pip setuptools 2>/dev/null || true \ && find /usr/local/lib/python3.12 -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true \ && find /usr/local/lib/python3.12 -type f -name "*.pyc" -o -name "*.pyo" -delete 2>/dev/null || true \ && find /usr/local/lib/python3.12 -type d -name "tests" -o -name "test" | xargs rm -rf 2>/dev/null || true # ── Non-root user ───────────────────────────────────────────────────────── # # The app runs as "user" (UID 1000) with NO sudo access. # The entrypoint handles any root-level setup (iptables, chown) via gosu. # RUN useradd -m -s /bin/bash -u 1000 user COPY entrypoint-slim.sh /app/entrypoint-slim.sh RUN chmod +x /app/entrypoint-slim.sh # ── NOTE: We do NOT set "USER user" here ────────────────────────────────── # # The container starts as root so the entrypoint can: # 1. Fix /home/user ownership on bind mounts # 2. Set up iptables egress rules (if configured) # 3. Drop privileges via gosu → runs the app as "user" # # If you don't need the egress firewall, you can add "USER user" here # and simplify the entrypoint — but gosu handles it cleanly either way. # ENV SHELL=/bin/bash EXPOSE 8000 # tini is PID 1 — reaps zombies and forwards signals cleanly. ENTRYPOINT ["/usr/bin/tini", "--", "/app/entrypoint-slim.sh"] CMD ["run"]