# Multi-stage Dockerfile for socktop webterm # This reduces the final image size significantly by separating build and runtime # ============================================================================ # Stage 1: Documentation Builder # ============================================================================ FROM rust:1.91-slim-bookworm AS docs-builder WORKDIR /build # Install required tools RUN apt-get update && \ apt-get install -y --no-install-recommends curl ca-certificates && \ rm -rf /var/lib/apt/lists/* # Install mdbook first RUN cargo install mdbook # Copy documentation source (includes theme files) COPY docs ./docs # Download Catppuccin theme CSS if not already present RUN if [ ! -f docs/theme/catppuccin.css ]; then \ curl -fsSL https://github.com/catppuccin/mdBook/releases/latest/download/catppuccin.css \ -o docs/theme/catppuccin.css && \ echo "Catppuccin CSS downloaded successfully"; \ else \ echo "Catppuccin CSS already present"; \ fi # Build documentation RUN cd docs && \ mdbook build && \ ls -la book/ # ============================================================================ # Stage 2: Rust Builder # ============================================================================ FROM rust:1.91-slim-bookworm AS rust-builder WORKDIR /build # Install build dependencies RUN apt-get update && \ apt-get install -y --no-install-recommends \ pkg-config \ libssl-dev \ && rm -rf /var/lib/apt/lists/* # Copy only dependency files first for better caching COPY Cargo.toml Cargo.lock ./ # Create dummy source to cache dependencies RUN mkdir src && \ echo "fn main() {}" > src/server.rs && \ echo "pub fn lib() {}" > src/lib.rs && \ cargo build --release && \ rm -rf src target/release/webterm-server target/release/deps/webterm* # Copy actual source code COPY src ./src COPY templates ./templates COPY static ./static COPY build.rs ./build.rs # Copy built documentation from docs-builder stage COPY --from=docs-builder /build/docs/book ./static/docs # Verify documentation was copied RUN ls -la ./static/docs/ && \ test -f ./static/docs/index.html || (echo "ERROR: Documentation index.html not found!" && exit 1) # Build the actual application (force rebuild by touching sources) RUN touch src/server.rs src/lib.rs && \ cargo build --release && \ strip target/release/webterm-server # ============================================================================ # Stage 3: Node.js Builder # ============================================================================ FROM node:20-slim AS node-builder WORKDIR /build # Copy package files COPY package.json package-lock.json ./ COPY static ./static # Install only production dependencies RUN npm ci --only=production && \ # Copy static files to node_modules for serving cp static/terminado-addon.js node_modules/ && \ cp static/bg.png node_modules/ && \ cp static/styles.css node_modules/ && \ cp static/terminal.js node_modules/ && \ cp static/favicon.png node_modules/ # ============================================================================ # Stage 4: Runtime Image # ============================================================================ FROM debian:trixie-slim # Avoid prompts from apt ENV DEBIAN_FRONTEND=noninteractive ENV TERM=xterm-256color # Install only runtime dependencies RUN apt-get update && \ apt-get upgrade -y && \ apt-get install -y --no-install-recommends \ # Runtime libraries libssl3 \ ca-certificates \ # For socktop packages curl \ gnupg2 \ # Shell and utilities bash \ procps \ # Health check curl \ && rm -rf /var/lib/apt/lists/* # Add socktop APT repository and install packages RUN curl -fsSL https://jasonwitty.github.io/socktop/KEY.gpg | \ gpg --dearmor -o /usr/share/keyrings/socktop-archive-keyring.gpg && \ echo "deb [signed-by=/usr/share/keyrings/socktop-archive-keyring.gpg] https://jasonwitty.github.io/socktop stable main" > /etc/apt/sources.list.d/socktop.list && \ apt-get update && \ apt-get install -y --no-install-recommends socktop socktop-agent && \ rm -rf /var/lib/apt/lists/* # Create application user (if not already exists from socktop packages) RUN id -u socktop &>/dev/null || useradd -m -s /bin/bash socktop && \ mkdir -p /home/socktop/.config/socktop && \ chown -R socktop:socktop /home/socktop # Set working directory WORKDIR /app # Copy built binary from rust-builder COPY --from=rust-builder /build/target/release/webterm-server /usr/local/bin/webterm-server # Copy templates and static files COPY --from=rust-builder /build/templates ./templates COPY --from=rust-builder /build/static ./static # Verify documentation is present in static/docs RUN ls -la ./static/docs/ && \ test -f ./static/docs/index.html || echo "WARNING: Documentation not found in static/docs" # Copy node_modules from node-builder COPY --from=node-builder /build/node_modules ./node_modules # Copy runtime scripts COPY docker/entrypoint.sh /entrypoint.sh COPY docker/init-config.sh /init-config.sh COPY docker/restricted-shell.sh /usr/local/bin/restricted-shell.sh RUN chmod +x /entrypoint.sh /init-config.sh /usr/local/bin/restricted-shell.sh # Expose ports # 8082 - webterm HTTP server # 3001 - socktop agent (if used) EXPOSE 8082 3001 # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8082/ || exit 1 # Create a wrapper script that detects if running as root or socktop user RUN echo '#!/bin/bash\n\ if [ "$(id -u)" -eq 0 ]; then\n\ # Running as root - use init-config.sh to set up and switch to socktop\n\ exec /init-config.sh "$@"\n\ else\n\ # Running as socktop user - directly execute entrypoint\n\ exec /entrypoint.sh "$@"\n\ fi' > /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh # Set entrypoint to the wrapper ENTRYPOINT ["/docker-entrypoint.sh"] # Default command - use restricted shell that only allows socktop commands CMD ["webterm-server", "--host", "0.0.0.0", "--port", "8082", "--command", "/usr/local/bin/restricted-shell.sh"]