# --------------------------------------------------------------------------- # APtool Docker image # --------------------------------------------------------------------------- # Uses the official Python 3.11 slim image as the base. "slim" is a minimal # Debian-based image (~45MB) that includes just enough to run Python -- no # compilers, man pages, or other extras. This keeps the image small. FROM python:3.11-slim # Set the working directory inside the container. All subsequent COPY, RUN, # and CMD instructions will execute relative to /app. WORKDIR /app # Copy requirements.txt first, BEFORE the app code. Docker caches each # layer, so if requirements.txt hasn't changed, "pip install" won't re-run # even if app.py changes. This speeds up rebuilds significantly. COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Now copy the application code and templates into the image. # sites.conf is baked in as a default but gets overridden at runtime # by the docker-compose volume mount (see docker-compose.yml). COPY app.py . COPY templates/ templates/ COPY sites.conf . # Create the uploads directory inside the container. At runtime this # gets overlaid by a bind mount from the host (./uploads:/app/uploads) # so that uploaded photos persist across container restarts. RUN mkdir -p uploads # Document that the app listens on port 5000. This is informational -- # the actual port mapping is configured in docker-compose.yml. EXPOSE 5000 # Run the app with gunicorn (production WSGI server) instead of Flask's # built-in dev server. Flags: # -w 2 -- 2 worker processes (handles concurrent requests) # -b 0.0.0.0:5000 -- bind to all interfaces on port 5000 # --access-logfile - -- print access logs to stdout (visible in docker logs) # app:app -- module:variable -- import "app" from app.py, use the # Flask instance named "app" CMD ["gunicorn", "-w", "2", "-b", "0.0.0.0:5000", "--access-logfile", "-", "app:app"]