Alpine Linux Will Trick You
Alpine Linux makes excellent Docker images. They’re tiny — a fraction of the size of Debian or Ubuntu images. They start fast. They have a small attack surface. And they have a handful of differences from “normal” Linux that will burn you exactly once each.
Here’s my collection.
The www-data home directory
On Debian/Ubuntu, the www-data user’s home directory is /var/www. On Alpine, it’s /home/www-data. This matters when:
-
SSH keys: If your PHP container needs to clone git repos over SSH, you need SSH keys for
www-data. SSH ignores the$HOMEenvironment variable and reads the home directory from/etc/passwd. Put the keys in/var/www/.ssh/and SSH will never find them on Alpine. -
git config: Same issue.
git config --globalwrites to~/.gitconfig, and~is/home/www-dataon Alpine.
The fix is an entrypoint script that copies SSH keys to the correct location with the correct ownership (uid 82 for www-data on Alpine) and permissions (600 — SSH refuses to use world-readable keys).
The git safe.directory scope
When running git operations as www-data on directories owned by another user, git complains about “dubious ownership.” The fix is git config safe.directory <path>. But where you set it matters:
--globalwrites to~/.gitconfig— which means it’s per-user and depends on the correct home directory (see above)--systemwrites to/etc/gitconfig— which works for all users regardless of home directory
In a Dockerfile, always use --system. It works at build time and at runtime, for any user.
Bind-mounted nginx configs
When you bind-mount an nginx config file from the host into a container:
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
Changes to the file on the host are visible inside the container (bind mounts are live). But nginx -s reload sometimes doesn’t pick up the changes — the inode hasn’t changed from nginx’s perspective.
The reliable fix: docker compose up -d --force-recreate. This recreates the container, which re-reads all mounted files. It’s heavier than a reload, but it always works.
This isn’t Alpine-specific, but it’s most commonly encountered with Alpine nginx images because they’re the default choice for static site serving.
PHP extensions
Alpine uses apk instead of apt. PHP extension installation is different:
# Debian
RUN apt-get update && apt-get install -y php8.3-tokenizer
# Alpine (with docker-php-ext-install)
RUN docker-php-ext-install tokenizer
# Alpine (with apk, for non-Docker-official images)
RUN apk add --no-cache php84-tokenizer
LinuxServer.io images (which we use for DokuWiki) don’t use the official PHP Docker images, so docker-php-ext-install isn’t available. You use apk directly, and the package names follow Alpine’s naming convention, not Debian’s.
The takeaway
None of these are bugs. They’re design decisions that make perfect sense in Alpine’s context. But if you’ve spent years on Debian-based systems, your muscle memory is wrong in small, subtle ways. Each one costs 15-30 minutes of debugging the first time you hit it.
Keep a list. I kept mine.