-
Notifications
You must be signed in to change notification settings - Fork 175
Expand file tree
/
Copy pathDockerfile.slim
More file actions
134 lines (116 loc) · 5.24 KB
/
Dockerfile.slim
File metadata and controls
134 lines (116 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# ============================================================================
# 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"]