**Refactor Docker and Compose for enhanced compatibility and efficiency**

- Restructured `Dockerfile` to include a dedicated frontend build stage and improved runtime configurations.
- Added new health checks and adjusted environment variables for better container management.
- Updated cron job configurations to leverage `/opt/venv` and align with new paths.
- Refined `docker-compose` files for multi-service setups, including better volume and environment variable handling.
- Migrated `cbwebreader-cron` service setup and improved restart policies across the board.
- Enhanced `.gitea` CI workflow to use updated tools (`uv`), streamline buildx processes, and ensure proper version extraction from metadata.
This commit is contained in:
ajurna committed 2026-10-13 10:25:07 +01:00
1 parent 9b9db755e6
commit 147a3a54ef
9 files changed
+122 -92

No files matched your search

-2
View File
@@ -3,7 +3,6 @@
!cbreader
!comic
!comic_auth
!static
!manage.py
!pyproject.toml
@@ -14,6 +13,5 @@
!package-lock.json
!package.json
!frontend
!uv.lock
/frontend/node_modules
/frontend/dist
+5 -3
View File
@@ -15,8 +15,10 @@ DB_DATABASE=cbwebreader
# https://github.com/jacobian/dj-database-url
DATABASE_URL=postgres://${DB_USER}:${DB_PASS}@${DB_HOST}/${DB_DATABASE}
#Path to your comics.
COMIC_BOOK_VOLUME=/media/plex/comics
# Path to your comics *inside* the container. Keep this as a container path.
COMIC_BOOK_VOLUME=/comics
# Host path that is mounted to the path above.
COMIC_BOOK_HOST_PATH=/media/plex/comics
STATIC_ROOT='/static'
@@ -32,4 +34,4 @@ MEDIA_ROOT='/media'
# DJANGO_RECAPTCHA_PUBLIC_KEY = ''
# Comment the following if not using a reverse proxy.
USE_X_FORWARDED_HOST=True
USE_X_FORWARDED_HOST=True
+5 -3
View File
@@ -15,8 +15,10 @@ DB_DATABASE=cbwebreader
# https://github.com/jacobian/dj-database-url
DATABASE_URL='postgres://${DB_USER}:${DB_PASS}@${DB_HOST}/${DB_DATABASE}'
# Path to your comics.
COMIC_BOOK_VOLUME=/path/to/comic/folder
# Path to your comics *inside* the container. Keep this as a container path.
COMIC_BOOK_VOLUME=/comics
# Host path that is mounted to the path above (this is the one that varies per machine).
COMIC_BOOK_HOST_PATH=/path/to/comic/folder
# Usually fine tp leave as is.
STATIC_ROOT=/static
@@ -29,4 +31,4 @@ MEDIA_ROOT='/media'
# UNRAR_TOOL = 'unrar.exe'
# Comment the following if not using a reverse proxy.
USE_X_FORWARDED_HOST=True
USE_X_FORWARDED_HOST=True
+8 -12
View File
@@ -12,25 +12,21 @@ jobs:
run: curl -fsSL https://get.docker.com | sh
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
- name: Set up Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: install pip
run: apt update && apt install -y python3-pip
- uses: abatilo/actions-poetry@v2
with:
poetry-version: "1.6.1"
- uses: actions/setup-node@v3
- uses: actions/checkout@v3
# Extract version from Poetry
- uses: actions/checkout@v4
- name: Install uv
run: pip install uv
# Extract version from the project metadata
- name: Get version
run: echo "POETRY_VERSION=$(poetry version --short)" >> $GITHUB_ENV
run: echo "PROJECT_VERSION=$(uv version --short)" >> $GITHUB_ENV
- name: Build and push Docker image
run: |
docker build -t ajurna/cbwebreader:latest -t ajurna/cbwebreader:${{ env.POETRY_VERSION }} .
docker push ajurna/cbwebreader:${{ env.POETRY_VERSION }}
docker build -t ajurna/cbwebreader:latest -t ajurna/cbwebreader:${{ env.PROJECT_VERSION }} .
docker push ajurna/cbwebreader:${{ env.PROJECT_VERSION }}
docker push ajurna/cbwebreader:latest
+42 -21
View File
@@ -1,36 +1,57 @@
FROM python:3.14-slim-trixie
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
ENV PYTHONFAULTHANDLER=1 \
PYTHONHASHSEED=random \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_DEFAULT_TIMEOUT=100 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PIP_NO_CACHE_DIR=1
ENV PATH="${PATH}:/root/.local/bin"
RUN mkdir /src
RUN mkdir /static
# ---------------------------------------------------------------------------
# Frontend build stage
# ---------------------------------------------------------------------------
FROM node:22-slim AS frontend
WORKDIR /src
COPY frontend/package.json frontend/package-lock.json ./
RUN npm ci
COPY frontend/ ./
RUN npm run build
RUN echo "deb http://ftp.uk.debian.org/debian trixie non-free non-free-firmware" > /etc/apt/sources.list.d/non-free.list
# ---------------------------------------------------------------------------
# Runtime stage
# ---------------------------------------------------------------------------
FROM python:3.14-slim-trixie
RUN apt update \
&& apt install -y npm cron unrar libmariadb-dev libpq-dev pkg-config swig curl
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
COPY . /src/
ENV PYTHONFAULTHANDLER=1 \
PYTHONHASHSEED=random \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
ENV UV_PROJECT_ENVIRONMENT=/opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# unrar lives in the non-free component; resolve the codename from the base image
# instead of hardcoding it.
RUN . /etc/os-release \
&& echo "deb http://deb.debian.org/debian ${VERSION_CODENAME} non-free non-free-firmware" \
> /etc/apt/sources.list.d/non-free.list \
&& apt update \
&& apt install -y --no-install-recommends cron unrar libmariadb-dev pkg-config \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /src
# Lockfiles first so dependency installation is cached independently of the source.
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev
RUN cd frontend \
&& npm install \
&& npm run build \
&& rm -r node_modules
COPY . /src/
COPY --from=frontend /src/dist /src/frontend/dist
COPY --from=frontend /src/webpack-stats.json /src/frontend/webpack-stats.json
RUN mkdir -p /static /media
RUN cat /src/cbreader/crontab >> /etc/crontab
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/')" || exit 1
CMD ["sh", "entrypoint.sh"]
+1 -1
View File
@@ -1 +1 @@
*/15 * * * * root /src/.venv/bin/python /src/manage.py scan_comics --settings=cbreader.settings.base
*/15 * * * * root /opt/venv/bin/python /src/manage.py scan_comics --settings=cbreader.settings.base
+24 -19
View File
@@ -1,64 +1,69 @@
version: "2.4"
services:
cbwebreader:
build: ../
image: ajurna/cbwebreader
env_file: .env
links:
- database
depends_on:
database:
condition: service_healthy
expose:
- 8000
volumes:
- ${COMIC_BOOK_VOLUME}:${COMIC_BOOK_VOLUME}
# COMIC_BOOK_VOLUME is the path *inside* the container; the host path is
# a separate variable so a Windows host path is not passed to Django.
- ${COMIC_BOOK_HOST_PATH}:${COMIC_BOOK_VOLUME}
- static_files:/static
- media_files:/media
- .env:/src/.env
command: /bin/bash entrypoint.sh
command: sh entrypoint.sh
restart: unless-stopped
cbwebreader-cron:
build: ../
image: ajurna/cbwebreader
env_file: .env
links:
- database
depends_on:
database:
condition: service_healthy
volumes:
- ${COMIC_BOOK_VOLUME}:${COMIC_BOOK_VOLUME}
- ${COMIC_BOOK_HOST_PATH}:${COMIC_BOOK_VOLUME}
- media_files:/media
- .env:/src/.env
command: /bin/bash entrypoint-cron.sh
command: sh entrypoint-cron.sh
restart: unless-stopped
database:
image: postgres:18-alpine
image: postgres:16-alpine
expose:
- 5432
volumes:
- ./data:/var/lib/postgresql/data
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $DB_USER -d $DB_DATABASE"]
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 5s
timeout: 10s
retries: 3
environment:
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASS}
- POSTGRES_DB=${DB_DATABASE}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASS}
POSTGRES_DB: ${DB_DATABASE}
restart: unless-stopped
nginx:
image: nginx
image: nginx:1-alpine
volumes:
- static_files:/static
- media_files:/media
- ./nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- 1337:80
- "${WEB_PORT:-8337}:80"
depends_on:
- cbwebreader
cbwebreader:
condition: service_healthy
restart: unless-stopped
volumes:
static_files:
media_files:
postgres_data:
+32 -28
View File
@@ -1,65 +1,69 @@
version: "2.4"
services:
cbwebreader:
build: .
image: ajurna/cbwebreader
env_file: .env
links:
- database
depends_on:
database:
condition: service_healthy
expose:
- 8000
volumes:
# - ${COMIC_BOOK_VOLUME}:/comics
- c:/comics:/comics
# COMIC_BOOK_VOLUME is the path *inside* the container; the host path is
# a separate variable so a Windows host path is not passed to Django.
- ${COMIC_BOOK_HOST_PATH}:${COMIC_BOOK_VOLUME}
- static_files:/static
- media_files:/media
- .env:/src/.env
command: /bin/bash /src/entrypoint.sh
command: sh entrypoint.sh
restart: unless-stopped
# cbwebreader-cron:
# build: .
# env_file: .env
# links:
# - database
# depends_on:
# database:
# condition: service_healthy
# volumes:
# - ${COMIC_BOOK_VOLUME}:${COMIC_BOOK_VOLUME}
# - media_files:/media
# - .env:/src/.env
# command: /bin/bash entrypoint-cron.sh
cbwebreader-cron:
build: .
image: ajurna/cbwebreader
env_file: .env
depends_on:
database:
condition: service_healthy
volumes:
- ${COMIC_BOOK_HOST_PATH}:${COMIC_BOOK_VOLUME}
- media_files:/media
- .env:/src/.env
command: sh entrypoint-cron.sh
restart: unless-stopped
database:
image: postgres:16-alpine
expose:
- 5432
volumes:
- /var/lib/postgresql/data
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $DB_USER -d $DB_DATABASE"]
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 5s
timeout: 10s
retries: 3
environment:
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASS}
- POSTGRES_DB=${DB_DATABASE}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASS}
POSTGRES_DB: ${DB_DATABASE}
restart: unless-stopped
nginx:
image: nginx
image: nginx:1-alpine
volumes:
- static_files:/static
- media_files:/media
- ./cbreader/settings/nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- 8337:80
- "${WEB_PORT:-8337}:80"
depends_on:
- cbwebreader
cbwebreader:
condition: service_healthy
restart: unless-stopped
volumes:
static_files:
media_files:
postgres_data:
+5 -3
View File
@@ -1,6 +1,8 @@
#!/usr/bin/env sh
uv run manage.py migrate --settings=cbreader.settings.base
set -e
uv run manage.py collectstatic --settings=cbreader.settings.base --noinput --clear
python manage.py migrate --settings=cbreader.settings.base
uv run gunicorn --workers 3 --bind 0.0.0.0:8000 cbreader.wsgi:application
python manage.py collectstatic --settings=cbreader.settings.base --noinput --clear
exec gunicorn --workers 3 --bind 0.0.0.0:8000 cbreader.wsgi:application