Files
webssh/deployment/Dockerfile
T
rabadjiyski c4a368d9f7 Release v0.6.0
Add database-backed user management and password reset tooling.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-23 19:21:52 +03:00

99 lines
4.1 KiB
Docker

# Multi-stage Dockerfile for WebSSH - Optimized for small image size
# Stage 1: Builder - compiles Python packages with build dependencies
FROM python:3.11-slim-bookworm AS builder
# Install build dependencies needed for compiling cryptography/pynacl on arm/v7
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
make \
python3-dev \
libffi-dev \
libssl-dev \
libsodium-dev \
curl \
ca-certificates \
binutils \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies into a virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt /tmp/
COPY src/ /tmp/src/
# Prefer prebuilt wheels when available to avoid building larger compiled artifacts
RUN SODIUM_INSTALL=system python -m pip install --no-cache-dir --prefer-binary -r /tmp/requirements.txt
# Strip compiled extensions and remove tests/pycache in venv to reduce size
RUN set -eux; \
# Strip native extensions to reduce size when possible
if command -v strip >/dev/null 2>&1; then \
find /opt/venv -type f -name '*.so' -print0 | xargs -0 -r strip --strip-unneeded || true; \
find /opt/venv -type f -executable -print0 | xargs -0 -r file | awk -F: '/ELF/{print $1}' | xargs -0 -r strip --strip-unneeded || true; \
fi; \
# Remove Python caches and compiled bytecode
find /opt/venv -type d -name '__pycache__' -print0 | xargs -0 -r rm -rf || true; \
find /opt/venv -type f -name '*.pyc' -print0 | xargs -0 -r rm -f || true; \
# Remove test folders from packages
find /opt/venv -type d \( -name 'tests' -o -name 'test' \) -print0 | xargs -0 -r rm -rf || true; \
# Remove static docs and unnecessary package metadata that are not needed at runtime
find /opt/venv -type d -name 'doc' -print0 | xargs -0 -r rm -rf || true; \
find /opt/venv -type d -name 'docs' -print0 | xargs -0 -r rm -rf || true; \
# Remove pip/setuptools/wheel from the venv to save space (not needed at runtime)
rm -rf /opt/venv/bin/pip* || true; \
find /opt/venv -path '*/site-packages/pip*' -print0 | xargs -0 -r rm -rf || true; \
find /opt/venv -path '*/site-packages/setuptools*' -print0 | xargs -0 -r rm -rf || true; \
find /opt/venv -path '*/site-packages/wheel*' -print0 | xargs -0 -r rm -rf || true; \
true
# Stage 2: official multi-arch Caddy image (amd64, arm64, arm/v7) used as the binary source
FROM caddy:2 AS caddy
# Stage 3: Final - minimal runtime image
FROM python:3.11-slim-bookworm
# Install only runtime dependencies (no build tools)
RUN apt-get update && apt-get install -y --no-install-recommends \
gosu \
libsodium23 \
libffi8 \
&& rm -rf /var/lib/apt/lists/*
# Copy Python packages from builder
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Security: Run as non-root user
RUN groupadd -r webssh && useradd -r -g webssh webssh
WORKDIR /app/src
# Copy application and static files (owned by webssh in one layer, no separate chown pass)
COPY --chown=webssh:webssh src/ /app/src/
COPY deployment/docker-entrypoint.sh /usr/local/bin/
COPY deployment/scripts/password-reset /usr/local/bin/password-reset
# Create .ssh directory for host keys
RUN mkdir -p /app/src/.ssh && chown webssh:webssh /app/src/.ssh
# Copy entrypoint
RUN chmod +x /usr/local/bin/docker-entrypoint.sh /usr/local/bin/password-reset
# Copy the Caddy binary from the official image
COPY --from=caddy /usr/bin/caddy /usr/local/bin/caddy
# Copy Caddyfile and static assets after package installation to avoid dpkg conffile prompts
COPY deployment/Caddyfile /etc/caddy/Caddyfile
COPY src/index.html /srv/
COPY src/help.html /srv/
COPY src/static/ /srv/static/
# Expose host-facing port for Caddy and internal service ports
EXPOSE 8080 8081 8082 8083
# Health check via Caddy (serving root) using Python (no curl required)
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import sys, urllib.request; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8080/', timeout=5).getcode() == 200 else 1)"
# Use shell form to see entrypoint errors
ENTRYPOINT /usr/local/bin/docker-entrypoint.sh