reduce production image size without runtime drift
Some checks failed
Upload latest backend image to GHCR / upload (push) Failing after 2m45s
Test Backend / Build and Test Backend (push) Failing after 2m3s
Upload latest frontend image to GHCR / upload (push) Failing after 13s
Test Frontend / Build and Test Frontend (push) Successful in 10m51s
Trivy Security Scans / Trivy Filesystem Scan (Source Code) (push) Failing after 1m43s
Trivy Security Scans / Trivy Docker Image Scan (Backend & Frontend) (push) Failing after 27s
Some checks failed
Upload latest backend image to GHCR / upload (push) Failing after 2m45s
Test Backend / Build and Test Backend (push) Failing after 2m3s
Upload latest frontend image to GHCR / upload (push) Failing after 13s
Test Frontend / Build and Test Frontend (push) Successful in 10m51s
Trivy Security Scans / Trivy Filesystem Scan (Source Code) (push) Failing after 1m43s
Trivy Security Scans / Trivy Docker Image Scan (Backend & Frontend) (push) Failing after 27s
This commit is contained in:
32
backend/.dockerignore
Normal file
32
backend/.dockerignore
Normal file
@@ -0,0 +1,32 @@
|
||||
.git
|
||||
.github
|
||||
.devcontainer
|
||||
.env
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.log
|
||||
*.sqlite3
|
||||
.coverage
|
||||
.pytest_cache/
|
||||
.mypy_cache/
|
||||
.ruff_cache/
|
||||
coverage.xml
|
||||
htmlcov/
|
||||
server/.pytest_cache
|
||||
server/.mypy_cache
|
||||
server/.ruff_cache
|
||||
server/.coverage
|
||||
server/coverage.xml
|
||||
server/htmlcov
|
||||
server/.hypothesis
|
||||
server/scheduler.log
|
||||
server/**/*.log
|
||||
server/tests/
|
||||
server/**/tests/
|
||||
server/test_*.py
|
||||
server/**/__pycache__/
|
||||
server/**/*.py[cod]
|
||||
server/media/
|
||||
server/staticfiles/
|
||||
server/.env
|
||||
server/.env.*
|
||||
@@ -1,8 +1,7 @@
|
||||
# Stage 1: Build stage with dependencies
|
||||
ARG PYTHON_IMAGE=python:3.13-slim
|
||||
|
||||
FROM ${PYTHON_IMAGE} AS builder
|
||||
|
||||
# Metadata labels
|
||||
LABEL maintainer="Voyage contributors" \
|
||||
version="0.10.0" \
|
||||
description="Voyage — the ultimate self-hosted travel companion." \
|
||||
@@ -14,64 +13,55 @@ LABEL maintainer="Voyage contributors" \
|
||||
org.opencontainers.image.vendor="Voyage contributors" \
|
||||
org.opencontainers.image.licenses="GPL-3.0"
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
VIRTUAL_ENV=/opt/venv
|
||||
|
||||
WORKDIR /code
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install system dependencies needed for build
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
postgresql-client \
|
||||
gdal-bin \
|
||||
build-essential \
|
||||
libgdal-dev \
|
||||
nginx \
|
||||
memcached \
|
||||
supervisor \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
libpq-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Python dependencies
|
||||
COPY ./server/requirements.txt /code/
|
||||
RUN pip install --upgrade pip \
|
||||
&& pip install --no-cache-dir -r requirements.txt
|
||||
COPY ./server/requirements.txt /tmp/requirements.txt
|
||||
|
||||
RUN python -m venv "$VIRTUAL_ENV" \
|
||||
&& "$VIRTUAL_ENV/bin/pip" install --upgrade pip \
|
||||
&& "$VIRTUAL_ENV/bin/pip" install --no-cache-dir --no-compile --prefer-binary -r /tmp/requirements.txt \
|
||||
&& find "$VIRTUAL_ENV" \( -type d -name '__pycache__' -o -type d -name 'tests' \) -prune -exec rm -rf '{}' + \
|
||||
&& find "$VIRTUAL_ENV" -type f \( -name '*.pyc' -o -name '*.pyo' \) -delete
|
||||
|
||||
# Stage 2: Final image with runtime dependencies
|
||||
FROM ${PYTHON_IMAGE}
|
||||
WORKDIR /code
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install runtime dependencies (including GDAL)
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
VIRTUAL_ENV=/opt/venv
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
postgresql-client \
|
||||
gdal-bin \
|
||||
libgdal-dev \
|
||||
libgdal36 \
|
||||
nginx \
|
||||
memcached \
|
||||
supervisor \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy Python packages from builder
|
||||
COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
|
||||
COPY --from=builder /usr/local/bin /usr/local/bin
|
||||
|
||||
# Copy project code and configs
|
||||
COPY --from=builder /opt/venv /opt/venv
|
||||
COPY ./server /code/
|
||||
COPY ./nginx.conf /etc/nginx/nginx.conf
|
||||
COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
COPY ./entrypoint.sh /code/entrypoint.sh
|
||||
|
||||
RUN chmod +x /code/entrypoint.sh \
|
||||
&& mkdir -p /code/static /code/media
|
||||
|
||||
# Collect static files
|
||||
RUN python3 manage.py collectstatic --noinput --verbosity 2
|
||||
RUN "$VIRTUAL_ENV/bin/python" manage.py collectstatic --noinput --verbosity 2
|
||||
|
||||
# Expose ports
|
||||
EXPOSE 80 8000
|
||||
|
||||
# Start with an entrypoint that runs init tasks then starts supervisord
|
||||
ENTRYPOINT ["/code/entrypoint.sh"]
|
||||
|
||||
# Start supervisord to manage processes
|
||||
CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||
|
||||
@@ -1,29 +1,45 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
# Function to check PostgreSQL availability
|
||||
# Helper to get the first non-empty environment variable
|
||||
get_env() {
|
||||
for var in "$@"; do
|
||||
value="${!var}"
|
||||
eval "value=\${$var:-}"
|
||||
if [ -n "$value" ]; then
|
||||
echo "$value"
|
||||
return
|
||||
printf '%s\n' "$value"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
check_postgres() {
|
||||
local db_host
|
||||
local db_user
|
||||
local db_name
|
||||
local db_pass
|
||||
|
||||
db_host=$(get_env PGHOST)
|
||||
db_host=${db_host:-localhost}
|
||||
db_port=$(get_env PGPORT)
|
||||
db_port=${db_port:-5432}
|
||||
db_user=$(get_env PGUSER POSTGRES_USER)
|
||||
db_name=$(get_env PGDATABASE POSTGRES_DB)
|
||||
db_pass=$(get_env PGPASSWORD POSTGRES_PASSWORD)
|
||||
|
||||
PGPASSWORD="$db_pass" psql -h "$db_host" -U "$db_user" -d "$db_name" -c '\q' >/dev/null 2>&1
|
||||
"${VIRTUAL_ENV:-/opt/venv}/bin/python" - "$db_host" "$db_port" "$db_user" "$db_name" "$db_pass" <<'PY' >/dev/null 2>&1
|
||||
import sys
|
||||
|
||||
import psycopg2
|
||||
|
||||
host, port, user, dbname, password = sys.argv[1:]
|
||||
|
||||
conn = psycopg2.connect(
|
||||
host=host,
|
||||
port=int(port),
|
||||
user=user,
|
||||
dbname=dbname,
|
||||
password=password,
|
||||
connect_timeout=1,
|
||||
)
|
||||
conn.close()
|
||||
PY
|
||||
}
|
||||
|
||||
|
||||
@@ -39,12 +55,12 @@ done
|
||||
# psql -h "$PGHOST" -U "$PGUSER" -d "$PGDATABASE" -f /app/backend/init-postgis.sql
|
||||
|
||||
# Apply Django migrations
|
||||
python manage.py migrate
|
||||
${VIRTUAL_ENV:-/opt/venv}/bin/python manage.py migrate
|
||||
|
||||
# Create superuser if environment variables are set and there are no users present at all.
|
||||
if [ -n "$DJANGO_ADMIN_USERNAME" ] && [ -n "$DJANGO_ADMIN_PASSWORD" ] && [ -n "$DJANGO_ADMIN_EMAIL" ]; then
|
||||
echo "Creating superuser..."
|
||||
python manage.py shell << EOF
|
||||
${VIRTUAL_ENV:-/opt/venv}/bin/python manage.py shell << EOF
|
||||
from django.contrib.auth import get_user_model
|
||||
from allauth.account.models import EmailAddress
|
||||
|
||||
@@ -76,7 +92,7 @@ fi
|
||||
|
||||
# Sync the countries and world travel regions
|
||||
# Sync the countries and world travel regions
|
||||
python manage.py download-countries
|
||||
${VIRTUAL_ENV:-/opt/venv}/bin/python manage.py download-countries
|
||||
if [ $? -eq 137 ]; then
|
||||
>&2 echo "WARNING: The download-countries command was interrupted. This is likely due to lack of memory allocated to the container or the host. Please try again with more memory."
|
||||
exit 1
|
||||
|
||||
@@ -8,7 +8,7 @@ stdout_logfile=/dev/stdout
|
||||
stderr_logfile=/dev/stderr
|
||||
|
||||
[program:gunicorn]
|
||||
command=/usr/local/bin/gunicorn main.wsgi:application --bind [::]:8000 --workers 2 --timeout 120
|
||||
command=/opt/venv/bin/gunicorn main.wsgi:application --bind [::]:8000 --workers 2 --timeout 120
|
||||
directory=/code
|
||||
autorestart=true
|
||||
stdout_logfile=/dev/stdout
|
||||
@@ -25,7 +25,7 @@ stdout_logfile_maxbytes=0
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[program:sync_visited_regions]
|
||||
command=/usr/local/bin/python3 /code/run_periodic_sync.py
|
||||
command=/opt/venv/bin/python /code/run_periodic_sync.py
|
||||
directory=/code
|
||||
autorestart=true
|
||||
stdout_logfile=/dev/stdout
|
||||
|
||||
@@ -2,3 +2,8 @@ node_modules
|
||||
.svelte-kit
|
||||
build
|
||||
.env
|
||||
.git
|
||||
.github
|
||||
.vscode
|
||||
coverage
|
||||
package-lock.json
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# Use the official Bun image as the build platform (supports linux/amd64 and linux/arm64 natively)
|
||||
ARG BUN_VERSION=1.3.10
|
||||
FROM oven/bun:${BUN_VERSION}-alpine AS builder
|
||||
|
||||
# Metadata labels for the Voyage image
|
||||
FROM oven/bun:${BUN_VERSION} AS deps
|
||||
|
||||
LABEL maintainer="Voyage contributors" \
|
||||
version="v0.12.0" \
|
||||
description="Voyage — the ultimate self-hosted travel companion." \
|
||||
@@ -13,60 +12,34 @@ LABEL maintainer="Voyage contributors" \
|
||||
org.opencontainers.image.url="https://voyage.app" \
|
||||
org.opencontainers.image.source="https://github.com/Alex-Wiesner/voyage" \
|
||||
org.opencontainers.image.vendor="Voyage contributors" \
|
||||
org.opencontainers.image.created="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
|
||||
org.opencontainers.image.licenses="GPL-3.0"
|
||||
|
||||
# The WORKDIR instruction sets the working directory for everything that will happen next
|
||||
WORKDIR /app
|
||||
|
||||
# Upgrade zlib to include Alpine security fixes
|
||||
RUN apk upgrade --no-cache zlib
|
||||
|
||||
# Copy package files first for better Docker layer caching
|
||||
COPY package.json bun.lock* ./
|
||||
|
||||
# Clean install all node modules using bun with frozen lockfile
|
||||
RUN bun install --frozen-lockfile
|
||||
|
||||
# Copy the rest of the application files
|
||||
FROM deps AS builder
|
||||
|
||||
COPY . .
|
||||
RUN rm -f .env \
|
||||
&& bun run build
|
||||
|
||||
# Remove the development .env file if present
|
||||
RUN rm -f .env
|
||||
|
||||
# Build SvelteKit app
|
||||
RUN bun run build
|
||||
|
||||
# Make startup script executable
|
||||
RUN chmod +x ./startup.sh
|
||||
|
||||
# Keep only production dependencies for runtime image
|
||||
RUN bun install --frozen-lockfile --production
|
||||
|
||||
# Runtime image contains only built app + runtime deps
|
||||
FROM node:22-alpine AS runtime
|
||||
FROM oven/bun:${BUN_VERSION} AS prod-deps
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Upgrade zlib and remove npm toolchain from runtime image
|
||||
RUN apk upgrade --no-cache zlib \
|
||||
&& rm -f /usr/local/bin/npm /usr/local/bin/npx \
|
||||
&& rm -rf /usr/local/lib/node_modules/npm /usr/local/lib/node_modules/corepack
|
||||
COPY package.json bun.lock* ./
|
||||
RUN bun install --frozen-lockfile --production
|
||||
|
||||
FROM gcr.io/distroless/nodejs22-debian12:nonroot AS runtime
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy build artifacts and production runtime dependencies
|
||||
COPY --from=builder /app/build ./build
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
COPY --from=prod-deps /app/node_modules ./node_modules
|
||||
COPY --from=builder /app/package.json ./package.json
|
||||
COPY --from=builder /app/startup.sh ./startup.sh
|
||||
|
||||
# Ensure startup script is executable
|
||||
RUN chmod +x ./startup.sh
|
||||
|
||||
# Change to non-root user for security
|
||||
USER node:node
|
||||
|
||||
# Expose the port that the app is listening on
|
||||
EXPOSE 3000
|
||||
|
||||
# Run startup.sh instead of the default command
|
||||
CMD ["./startup.sh"]
|
||||
CMD ["build/index.js"]
|
||||
|
||||
Reference in New Issue
Block a user