Files
newznab-tmux-NNTmux/DOCKER.md
T
2026-06-22 21:50:18 +02:00

14 KiB

Docker Development Environment

Docker / Sail setup for NNTmux — a Laravel 12 Usenet indexer.

Prerequisites

  • Docker & Docker Compose v2.22+ (the Makefile uses pull --ignore-buildable)
  • A GitHub (scopeless) token for Composer private packages
  • Laravel Sail (included as a dev dependency)

Quick Start

# 1. Copy and configure your environment
cp .env.example .env
# Edit .env — set DB credentials, COMPOSER_AUTH, and uncomment the
# "Docker service hostnames" block at the bottom.

# 2. Build and start
make build
make up

# 3. Install NNTmux (first run only)
make artisan cmd="nntmux:install"

# 4. Build frontend assets
make npm-build

Environment Configuration

When running via Docker, uncomment the Docker service hostnames block at the bottom of .env so the app resolves container names:

DB_HOST=mariadb
REDIS_HOST=redis
MANTICORESEARCH_HOST=manticore
ELASTICSEARCH_HOST=elasticsearch
MAIL_HOST=mailpit
MAIL_PORT=1025

Search Engine Selection

Set COMPOSE_PROFILES in .env to match your SEARCH_DRIVER:

SEARCH_DRIVER COMPOSE_PROFILES
manticore manticore
elasticsearch elasticsearch

Only the selected search service container will start. The other remains dormant and consumes no resources.

ManticoreSearch Image Updates

Both docker-compose.yml and docker-compose.yml.prod-dist intentionally use the unpinned manticoresearch/manticore image. This keeps ManticoreSearch on the latest published Docker image whenever images are pulled, but it also means production can receive ManticoreSearch changes without a repository diff.

Before pulling and restarting production, validate the new image in staging by creating indexes, indexing a small batch, and running representative searches, filters, sorting, pagination, deletes, suggestions, fuzzy searches, and any raw SQL diagnostics/reconciliation commands.

Manticore Search 27.1.5 is part of the 25.x-27.x release line that introduced built-in authentication/authorization, sharded tables, conversational search, vector-search improvements, and replication layout changes. The app does not enable Manticore auth by default, but if auth is enabled on the server set either MANTICORESEARCH_USERNAME/MANTICORESEARCH_PASSWORD or MANTICORESEARCH_TOKEN before running the app, index-creation commands, or raw HTTP diagnostics. Roll auth out in staging first; anonymous local Docker usage continues to work with these variables blank.

Native Ubuntu/Debian package installs are managed by systemd, not Docker. If the package install fails while starting manticore.service, especially with Status: "Replaying binlogs..." and accept() failed ... Too many open files, or if searchd prints a backtrace during binlog replay, use docs/manticore-ubuntu-package.md to raise the service file descriptor limit, preserve crash evidence, quarantine bad binlogs if needed, and finish dpkg configuration.

Make Targets

Run make or make help to see all targets, grouped by section.

Lifecycle

Target Description
make up Start all services (detached)
make down Stop all services
make restart Restart all services
make recreate Force-recreate containers without rebuilding
make build Build the app image (cached)
make rebuild Pull fresh base images + --no-cache build + force-recreate
make pull Pull latest enabled-profile base images (skips buildable ones)
make update Infra-only: pull + --pull build + restart
make upgrade App upgrade: update + composer + npm + migrate + caches
make fresh Destroy volumes, pull, rebuild, recreate (DATA LOSS)

Development

Target Description
make shell Bash shell in the app container
make root-shell Root bash shell in the app container
make tinker Laravel Tinker REPL
make artisan cmd= Run any artisan command
make migrate Run pending migrations
make migrate-fresh Drop all tables and re-migrate (DATA LOSS)
make seed Run database seeders
make cache-clear Clear config / route / view / app caches (and stale bootstrap/cache files)
make optimize Dev-safe: warm view cache + spatie/laravel-data
make optimize-deploy Production: also caches config/routes (bakes absolute paths)
make queue-work Foreground queue worker
make queue-restart Signal queue workers to restart
make tmux-start Start the NNTmux tmux processing engine
make tmux-stop Stop the tmux processing engine
make tmux-attach Attach to the running tmux session
make horizon Show Horizon queue status
make fix-permissions Chown project to host UID + register git safe.directory
make fix-permissions Chown project to sail + register git safe.directory

Testing & Quality

Target Description
make test PHPUnit tests (filter=Name optional)
make pint Pint formatter on dirty files
make pint-all Pint formatter on all files
make phpstan PHPStan static analysis (2G memory limit)
make rector Rector dry-run (no changes)
make rector-fix Apply Rector refactorings

Frontend & Types

Target Description
make npm-build npm install + npm run build
make npm-dev Start Vite dev server
make ts-types Regenerate TypeScript types
make ts-types-check CI: fail if generated types drift
make data-cache Cache spatie/laravel-data structures

Logs & Status

Target Description
make logs Tail all container logs (or SERVICE=name for one)
make tail-laravel Tail storage/logs/laravel.log inside the app container
make status / ps Show running containers
make top Show processes inside each container
make images Show images used by each service
make health Healthcheck status per service

Cleanup

Target Description
make clean Prune stopped containers / dangling images
make nuke Remove ALL project containers, images, volumes (DATA LOSS)

Flags

These can be combined with the targets above:

Flag Effect
FORCE=1 Skip confirmation prompts on fresh, nuke, migrate-fresh (CI use)
MAINTENANCE=1 Wrap upgrade migrations in artisan down / artisan up
SERVICE=name Restrict logs to a specific compose service
CMD="…" Free-form command for artisan (alternative to cmd=)
filter=Name Pass --filter=Name to make test

Examples:

make fresh FORCE=1                      # non-interactive teardown + rebuild
make upgrade MAINTENANCE=1              # zero-downtime-ish upgrade with maint mode
make logs SERVICE=mariadb               # tail only mariadb
make test filter=ReleaseSearchTest      # run a single test

Note: make pull / update / rebuild use docker compose pull --ignore-buildable, which only pulls images for services in the active COMPOSE_PROFILES and skips images that are built locally (e.g. sail-8.5/app). Requires Docker Compose v2.22+.

You can also use ./sail directly for anything not covered above — unknown commands are passed through to docker compose.

Networking

Sail creates a Docker network called sail with these default port mappings:

Service Container Port Host Port (default)
HTTP (nginx) 80 APP_PORT (80)
Vite 5173 VITE_PORT (5173)
MariaDB 3306 FORWARD_DB_PORT (3306)
Redis 6379 FORWARD_REDIS_PORT (6379)
Manticore SQL 9306 9306
Manticore HTTP 9308 9308
Elasticsearch 9200 9200
Mailpit SMTP 1025 FORWARD_MAILPIT_PORT (1025)
Mailpit Web 8025 FORWARD_MAILPIT_DASHBOARD_PORT (8025)

If a port is already in use on your host, change the corresponding FORWARD_* / APP_PORT variable in .env.

Database

On first run MariaDB creates a user/database from your .env:

DB_USERNAME=
DB_PASSWORD=
DB_DATABASE=nntmux

Import an existing SQL dump or run make artisan cmd="nntmux:install" for a fresh installation.

Backups

Add this optional service to docker-compose.yml for automated MariaDB backups:

    backup:
        image: fradelg/mysql-cron-backup
        depends_on:
            - mariadb
        restart: always
        volumes:
            - ./docker/backups:/backup
        environment:
            - MYSQL_USER=${DB_USERNAME}
            - MYSQL_PASS=${DB_PASSWORD}
            - MYSQL_DB=${DB_DATABASE}
            - CRON_TIME=0 3 * * *
            - MYSQL_HOST=mariadb
            - MYSQL_PORT=3306
            - TIMEOUT=10s
            - GZIP_LEVEL=9
            - MAX_BACKUPS=5
            - INIT_BACKUP=0
            - EXIT_BACKUP=1
        networks:
            - sail

Production

For production deployments use docker-compose.yml.prod-dist as a starting point. It includes separate webapp, worker, and scheduler services and uses the root Dockerfile (FrankenPHP-based).

cp docker-compose.yml.prod-dist docker-compose.yml
# Edit .env for production settings
docker compose up -d

Troubleshooting

Problem Solution
Permission errors on storage/ make fix-permissions (chowns project to your host UID)
composer install permission denied make fix-permissions, then re-run make composer-install
npm/ncu EACCES on host (package.json) make fix-permissions chowns to host UID, not container sail
fatal: detected dubious ownership (git) make fix-permissions registers safe.directory in the container
Container sail UID ≠ host UID Set WWWUSER=$(id -u) / WWWGROUP=$(id -g) in .env, then make rebuild
Host artisan/npm fails with /var/www/html/... paths Stale bootstrap/cache/config.php from a container config:cache. Run make cache-clear and prefer make optimize (dev-safe) over optimize-deploy in development
Port already in use Change APP_PORT, FORWARD_DB_PORT, etc. in .env
Containers won't start make logs to inspect, or make rebuild to start fresh
Stale images after upgrade make update (pulls base images + rebuild + restart)
Need a completely clean slate make fresh (destroys all volumes; add FORCE=1 for non-interactive)
CI / scripted teardown make fresh FORCE=1 or make nuke FORCE=1 to skip prompts
supervisorctl not connecting make root-shell then supervisorctl status to verify socket path

Nginx (GetPageSpeed + Brotli)

The app container's nginx is installed from the GetPageSpeed apt repo (stable branch) instead of the Ubuntu archive. This gives us a current nginx build with ABI-matched dynamic modules. The nginx-module-brotli package is installed and the brotli directives in docker/8.5/nginx.conf are enabled by default (gzip is kept as a fallback for clients without br support).

The repo is apt-pinned at priority 1001 via /etc/apt/preferences.d/getpagespeed-nginx.pref so nginx and module packages always resolve from GetPageSpeed even if Ubuntu publishes a newer version. A nginx -t is run during docker build to fail fast on any config drift.

To revert to stock Ubuntu nginx, remove the GetPageSpeed apt key, list, and preferences entries from docker/8.5/Dockerfile, drop nginx-module-brotli from the install line, re-comment the brotli block in docker/8.5/nginx.conf, and run make rebuild.