diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..47521a1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +# Use Python 3.11 slim image +FROM python:3.11-slim + +# Set working directory +WORKDIR /app + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + FLASK_APP=app.py \ + FLASK_ENV=production + +# Install system dependencies +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + gcc \ + python3-dev \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Copy requirements first to leverage Docker cache +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application +COPY . . + +# Create volume for persistent database storage +VOLUME ["/app/instance"] + +# Run as non-root user +RUN useradd -m myuser +RUN chown -R myuser:myuser /app +USER myuser + +# Expose port +EXPOSE 5000 + +# Command to run the application +CMD ["gunicorn", "-c", "gunicorn.conf.py", "app:app"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..81071e9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +services: + web: + build: . + ports: + - "5000:5000" + volumes: + - ./instance:/app/instance + env_file: + - .env + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s diff --git a/gunicorn.conf.py b/gunicorn.conf.py new file mode 100644 index 0000000..a463aca --- /dev/null +++ b/gunicorn.conf.py @@ -0,0 +1,38 @@ +# Gunicorn configuration file +import multiprocessing + +# Server socket +bind = "0.0.0.0:5000" +backlog = 2048 + +# Worker processes +workers = multiprocessing.cpu_count() * 2 + 1 +worker_class = "sync" +worker_connections = 1000 +timeout = 30 +keepalive = 2 + +# Logging +accesslog = "-" +errorlog = "-" +loglevel = "info" + +# Process naming +proc_name = "butter-garden" + +# Server mechanics +daemon = False +pidfile = None +umask = 0 +user = None +group = None +tmp_upload_dir = None + +# SSL +keyfile = None +certfile = None + +# Security +limit_request_line = 4096 +limit_request_fields = 100 +limit_request_field_size = 8190