feat: initial core-images repository

Consolidated container image definitions for the host-uk ecosystem,
producing both Docker images and TIM bundles from a single source.

Images:
- developer: Full-fat dev environment with Claude Code CLI, PHP 8.4,
  Node.js, Go, Python, and 100+ tools (ghcr.io/host-uk/core-dev)
- server-php: Production Alpine + Nginx + PHP-FPM with multi-stage
  builds for dev/prod targets (ghcr.io/host-uk/server-php)

Includes:
- Taskfile for local builds (docker + tim)
- GitHub Actions workflow for multi-arch builds
- Borgfiles for future TIM bundle generation

Consolidates docker-developer and docker-server-php repositories.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Snider 2026-01-28 17:27:17 +00:00
commit dafabd714c
10 changed files with 1025 additions and 0 deletions

106
.github/workflows/build.yml vendored Normal file
View file

@ -0,0 +1,106 @@
name: Build Images
on:
push:
branches: [main]
tags: ['v*']
pull_request:
branches: [main]
workflow_dispatch:
env:
REGISTRY: ghcr.io
jobs:
# ============================================================
# Build Docker Images
# ============================================================
docker:
name: Docker (${{ matrix.image }})
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
matrix:
image:
- developer
- server-php
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/host-uk/${{ matrix.image == 'developer' && 'core-dev' || matrix.image }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: ./${{ matrix.image }}
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ============================================================
# Build TIM Bundles (when core build --type tim is ready)
# ============================================================
# tim:
# name: TIM (${{ matrix.image }})
# runs-on: ubuntu-latest
# needs: docker
#
# strategy:
# matrix:
# image: [developer, server-php]
# os: [linux, darwin]
# arch: [amd64, arm64]
#
# steps:
# - uses: actions/checkout@v4
#
# - name: Install Core
# run: |
# curl -fsSL https://github.com/host-uk/core/releases/latest/download/core-linux-amd64 -o /usr/local/bin/core
# chmod +x /usr/local/bin/core
#
# - name: Build TIM
# run: |
# core build --type tim \
# --borgfile ./${{ matrix.image }}/Borgfile \
# --os ${{ matrix.os }} \
# --arch ${{ matrix.arch }} \
# -o ./dist/${{ matrix.image }}-${{ matrix.os }}-${{ matrix.arch }}.tim
#
# - name: Upload artifact
# uses: actions/upload-artifact@v4
# with:
# name: ${{ matrix.image }}-${{ matrix.os }}-${{ matrix.arch }}
# path: ./dist/*.tim

19
.gitignore vendored Normal file
View file

@ -0,0 +1,19 @@
# Build outputs
dist/
*.tim
*.stim
# IDE
.idea/
.vscode/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Temporary
tmp/
*.tmp
*.log

78
README.md Normal file
View file

@ -0,0 +1,78 @@
# core-images
Container images for the host-uk ecosystem. Each image produces dual outputs:
- **Docker image**`ghcr.io/host-uk/<name>`
- **TIM bundle**`<name>-<os>-<arch>.tim`
## Images
| Image | Purpose | Docker | TIM |
|-------|---------|--------|-----|
| `developer` | Full-fat dev environment (100+ tools) | `ghcr.io/host-uk/core-dev` | `core-dev.tim` |
| `server-php` | Alpine + Nginx + PHP-FPM | `ghcr.io/host-uk/server-php` | `server-php.tim` |
## Usage
### Docker
```bash
# Developer environment
docker run -it ghcr.io/host-uk/core-dev
# PHP server
docker run -p 80:80 ghcr.io/host-uk/server-php
```
### TIM (Docker-free via Core)
```bash
# Install dev environment
core dev install
# Or run directly
core run core-dev.tim
# Run PHP server
core run server-php.tim -p 80:80
```
## Building
```bash
# Requires: task (taskfile.dev)
# Build all
task build
# Build specific image
task build:developer
task build:server-php
# Build TIM only
task build:developer:tim
# Build Docker only
task build:developer:docker
```
## Structure
```
core-images/
├── developer/ # core-dev.tim - Full dev environment
│ ├── Dockerfile
│ ├── Borgfile
│ └── config/
├── server-php/ # server-php.tim - Nginx + PHP-FPM
│ ├── Dockerfile
│ ├── Borgfile
│ └── config/
└── base/ # Shared base configurations
└── alpine/
```
## Sources
Consolidated from:
- [docker-developer](https://github.com/host-uk/docker-developer)
- [docker-server-php](https://github.com/host-uk/docker-server-php)

124
Taskfile.yaml Normal file
View file

@ -0,0 +1,124 @@
version: '3'
vars:
REGISTRY: ghcr.io/host-uk
VERSION:
sh: git describe --tags --always --dirty 2>/dev/null || echo "dev"
tasks:
default:
desc: List available tasks
cmds:
- task --list
# ============================================================
# Build All
# ============================================================
build:
desc: Build all images (Docker + TIM)
deps:
- build:developer
- build:server-php
build:docker:
desc: Build all Docker images
deps:
- build:developer:docker
- build:server-php:docker
build:tim:
desc: Build all TIM bundles
deps:
- build:developer:tim
- build:server-php:tim
# ============================================================
# Developer Image
# ============================================================
build:developer:
desc: Build developer image (Docker + TIM)
deps:
- build:developer:docker
- build:developer:tim
build:developer:docker:
desc: Build developer Docker image
dir: developer
cmds:
- |
docker build \
--tag {{.REGISTRY}}/core-dev:{{.VERSION}} \
--tag {{.REGISTRY}}/core-dev:latest \
--build-arg VERSION={{.VERSION}} \
.
build:developer:tim:
desc: Build developer TIM bundle
dir: developer
cmds:
- echo "TODO: core build --type tim -o ../dist/core-dev-{{OS}}-{{ARCH}}.tim"
push:developer:
desc: Push developer image to registry
cmds:
- docker push {{.REGISTRY}}/core-dev:{{.VERSION}}
- docker push {{.REGISTRY}}/core-dev:latest
# ============================================================
# Server PHP Image
# ============================================================
build:server-php:
desc: Build server-php image (Docker + TIM)
deps:
- build:server-php:docker
- build:server-php:tim
build:server-php:docker:
desc: Build server-php Docker image
dir: server-php
cmds:
- |
docker build \
--tag {{.REGISTRY}}/server-php:{{.VERSION}} \
--tag {{.REGISTRY}}/server-php:latest \
--build-arg VERSION={{.VERSION}} \
.
build:server-php:tim:
desc: Build server-php TIM bundle
dir: server-php
cmds:
- echo "TODO: core build --type tim -o ../dist/server-php-{{OS}}-{{ARCH}}.tim"
push:server-php:
desc: Push server-php image to registry
cmds:
- docker push {{.REGISTRY}}/server-php:{{.VERSION}}
- docker push {{.REGISTRY}}/server-php:latest
# ============================================================
# Release
# ============================================================
release:
desc: Build and push all images
cmds:
- task: build
- task: push:developer
- task: push:server-php
# ============================================================
# Utilities
# ============================================================
clean:
desc: Remove build artifacts
cmds:
- rm -rf dist/
- docker rmi {{.REGISTRY}}/core-dev:{{.VERSION}} || true
- docker rmi {{.REGISTRY}}/server-php:{{.VERSION}} || true
dist:
desc: Create dist directory
cmds:
- mkdir -p dist
status:
- test -d dist

44
developer/Borgfile Normal file
View file

@ -0,0 +1,44 @@
# Borgfile for core-dev TIM bundle
# This will be processed by: core build --borgfile -o core-dev.tim
# Base from the Docker build output or direct Alpine
FROM alpine:3.22
# Core binary (the orchestrator)
ADD https://github.com/host-uk/core/releases/latest/download/core-linux-${ARCH} /usr/local/bin/core
# ============================================================
# AI / LLM Assistants
# ============================================================
ADD https://github.com/anthropics/claude-code/releases/latest/download/claude-linux-${ARCH} /usr/local/bin/claude
# ADD gemini, aider, ollama, llm...
# ============================================================
# Version Control
# ============================================================
ADD https://github.com/cli/cli/releases/latest/download/gh_linux_${ARCH}.tar.gz /tmp/gh.tar.gz
# Extract and install gh...
# ============================================================
# Languages / Runtimes
# ============================================================
# FrankenPHP - embedded PHP runtime
ADD https://github.com/dunglas/frankenphp/releases/latest/download/frankenphp-linux-${ARCH} /usr/local/bin/frankenphp
# Node.js
ADD https://nodejs.org/dist/latest-lts/node-linux-${ARCH}.tar.xz /tmp/node.tar.xz
# Go, Python, Rust, etc...
# ============================================================
# Configuration
# ============================================================
ADD config/zshrc /etc/skel/.zshrc
ADD config/starship.toml /etc/skel/.config/starship.toml
ADD config/aliases.sh /etc/profile.d/aliases.sh
# ============================================================
# OCI Config
# ============================================================
# Generated automatically by core build
# Sets up proper entrypoint, env vars, capabilities

219
developer/Dockerfile Normal file
View file

@ -0,0 +1,219 @@
# ============================================================
# Docker Developer - Claude-Focused Alpine Dev Image
#
# A kitchen-sink developer environment optimized for
# AI-assisted development with Claude Code CLI.
#
# Build: docker build -t docker-developer .
# Run: docker run -it docker-developer
# ============================================================
ARG ALPINE_VERSION=3.22
# ============================================================
# Developer - Full development environment
# ============================================================
FROM alpine:${ALPINE_VERSION} AS developer
LABEL maintainer="Snider <snider@host.uk.com>"
LABEL org.opencontainers.image.source="https://github.com/host-uk/docker-developer"
LABEL org.opencontainers.image.description="Claude-focused Alpine developer environment"
LABEL org.opencontainers.image.licenses="EUPL-1.2"
LABEL org.opencontainers.image.vendor="Host UK"
LABEL org.opencontainers.image.title="Docker Developer"
# Environment variables
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
ENV TERM=xterm-256color
ENV EDITOR=vim
ENV SHELL=/bin/zsh
ENV GOPATH=/root/go
ENV PATH="/root/go/bin:/root/.local/bin:/root/.composer/vendor/bin:/usr/local/bin:${PATH}"
# ============================================================
# Core System Tools
# ============================================================
RUN apk add --no-cache \
# Shell and basics
bash \
zsh \
zsh-vcs \
curl \
wget \
ca-certificates \
# Build tools
make \
cmake \
ninja \
meson \
gcc \
g++ \
musl-dev \
linux-headers \
# Essential utilities
git \
git-lfs \
openssh-client \
gnupg \
less \
tree \
ncdu \
htop \
# Editors
vim \
nano \
# Text processing
jq \
yq \
# Search tools
ripgrep \
fzf \
# Modern CLI tools (from Alpine packages)
bat \
eza \
fd \
# Terminal multiplexer
tmux \
# Compression
zip \
unzip \
tar \
gzip \
xz \
# Networking
bind-tools \
iputils \
# Process management
shadow \
# Template processing
gettext \
# For starship
starship
# ============================================================
# Node.js Ecosystem
# ============================================================
RUN apk add --no-cache \
nodejs \
npm
# Install global Node.js packages
RUN npm install -g \
@anthropic-ai/claude-code \
typescript \
ts-node \
pnpm \
yarn
# ============================================================
# Python Ecosystem
# ============================================================
RUN apk add --no-cache \
python3 \
py3-pip \
python3-dev
# Install Python tools
RUN pip3 install --break-system-packages \
pipx \
uv \
ipython \
httpie
# ============================================================
# PHP Ecosystem
# ============================================================
RUN apk add --no-cache \
php84 \
php84-phar \
php84-mbstring \
php84-openssl \
php84-curl \
php84-iconv \
php84-tokenizer \
php84-dom \
php84-xml \
php84-xmlwriter \
php84-simplexml \
php84-ctype \
php84-fileinfo \
php84-json \
php84-posix \
php84-pcntl \
php84-zip
# Create php symlink
RUN ln -sf /usr/bin/php84 /usr/bin/php
# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- \
--install-dir=/usr/bin --filename=composer
# Install PHP dev tools globally
RUN composer global require --no-interaction \
phpunit/phpunit:^11.0 \
phpstan/phpstan:^2.0 \
squizlabs/php_codesniffer:^3.0 \
friendsofphp/php-cs-fixer:^3.0
# ============================================================
# Go Ecosystem
# ============================================================
RUN apk add --no-cache go
# Install Go tools (pinned to versions compatible with Go 1.24)
RUN go install golang.org/x/tools/gopls@v0.17.1 && \
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
# ============================================================
# Database Clients
# ============================================================
RUN apk add --no-cache \
postgresql16-client \
mariadb-client \
redis \
sqlite
# ============================================================
# Container Tools
# ============================================================
RUN apk add --no-cache docker-cli
# ============================================================
# Git Enhancements
# ============================================================
RUN apk add --no-cache github-cli lazygit
# ============================================================
# Oh-My-Zsh Installation
# ============================================================
RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
# Install zsh plugins
RUN git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-/root/.oh-my-zsh/custom}/plugins/zsh-autosuggestions && \
git clone https://github.com/zsh-users/zsh-syntax-highlighting ${ZSH_CUSTOM:-/root/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
# ============================================================
# Configuration Files
# ============================================================
COPY --chmod=644 config/zshrc /root/.zshrc
COPY --chmod=644 config/starship.toml /root/.config/starship.toml
COPY --chmod=644 config/tmux.conf /root/.tmux.conf
# ============================================================
# Entrypoint and Hooks
# ============================================================
COPY --chmod=755 scripts/entrypoint.sh /usr/local/bin/entrypoint.sh
COPY --chmod=755 scripts/hooks.sh /usr/local/bin/hooks.sh
# Create config directories
RUN mkdir -p /root/.config/docker-developer/hooks/pre-start \
/root/.config/docker-developer/hooks/post-start \
/root/.claude \
/workspace
WORKDIR /workspace
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["/bin/zsh"]

View file

@ -0,0 +1,56 @@
# Core-dev shell aliases
# Navigation
alias ..='cd ..'
alias ...='cd ../..'
alias ll='eza -la --icons --git'
alias la='eza -la --icons'
alias lt='eza --tree --level=2 --icons'
# Git
alias g='git'
alias gs='git status'
alias gd='git diff'
alias gc='git commit'
alias gp='git push'
alias gl='git log --oneline -20'
alias gco='git checkout'
alias gb='git branch'
alias lg='lazygit'
# Docker
alias d='docker'
alias dc='docker compose'
alias dps='docker ps'
alias di='docker images'
alias dex='docker exec -it'
alias ld='lazydocker'
# Kubernetes
alias k='kubectl'
alias kgp='kubectl get pods'
alias kgs='kubectl get svc'
alias kgd='kubectl get deployments'
# PHP/Laravel
alias art='php artisan'
alias sail='./vendor/bin/sail'
alias pest='./vendor/bin/pest'
alias pint='./vendor/bin/pint'
# Core
alias c='core'
alias cdev='core dev'
alias cbuild='core build'
alias crun='core run'
# Misc
alias cat='bat'
alias find='fd'
alias grep='rg'
alias top='btop'
alias vim='nvim'
alias vi='nvim'
# FZF integrations
alias fzp='fzf --preview "bat --color=always {}"'

View file

@ -0,0 +1,90 @@
# Starship prompt configuration for core-dev
# https://starship.rs/config/
format = """
$username\
$hostname\
$directory\
$git_branch\
$git_status\
$php\
$nodejs\
$golang\
$python\
$rust\
$docker_context\
$kubernetes\
$cmd_duration\
$line_break\
$character"""
[character]
success_symbol = "[](bold green)"
error_symbol = "[](bold red)"
[username]
style_user = "bold blue"
format = "[$user]($style) "
show_always = false
[hostname]
ssh_only = true
format = "@ [$hostname](bold yellow) "
[directory]
truncation_length = 3
truncate_to_repo = true
style = "bold cyan"
[git_branch]
symbol = " "
style = "bold purple"
format = "[$symbol$branch]($style) "
[git_status]
format = '([\[$all_status$ahead_behind\]]($style) )'
style = "bold red"
[php]
symbol = " "
format = "[$symbol($version )]($style)"
style = "147"
[nodejs]
symbol = " "
format = "[$symbol($version )]($style)"
style = "bold green"
[golang]
symbol = " "
format = "[$symbol($version )]($style)"
style = "bold cyan"
[python]
symbol = " "
format = "[$symbol($version )]($style)"
style = "bold yellow"
[rust]
symbol = " "
format = "[$symbol($version )]($style)"
style = "bold red"
[docker_context]
symbol = " "
format = "[$symbol$context]($style) "
style = "blue bold"
only_with_files = true
[kubernetes]
symbol = "☸ "
format = '[$symbol$context( \($namespace\))]($style) '
style = "cyan bold"
disabled = false
[cmd_duration]
min_time = 2_000
format = "took [$duration](bold yellow) "
[line_break]
disabled = false

44
server-php/Borgfile Normal file
View file

@ -0,0 +1,44 @@
# Borgfile for server-php TIM bundle
# This will be processed by: core build --borgfile -o server-php.tim
# Base from Alpine
FROM alpine:3.22
# ============================================================
# PHP Runtime (FrankenPHP or PHP-FPM)
# ============================================================
ADD https://github.com/dunglas/frankenphp/releases/latest/download/frankenphp-linux-${ARCH} /usr/local/bin/frankenphp
# Or traditional PHP-FPM setup
# ADD php${PHP_VERSION} /usr/bin/php
# ADD php${PHP_VERSION}-fpm /usr/sbin/php-fpm
# ============================================================
# Nginx (optional, FrankenPHP has Caddy built-in)
# ============================================================
# ADD nginx /usr/sbin/nginx
# ADD config/nginx/ /etc/nginx/
# ============================================================
# PHP Extensions
# ============================================================
# Common extensions bundled with FrankenPHP
# Additional extensions via PHP_EXTENSIONS env var
# ============================================================
# Configuration
# ============================================================
ADD config/php-fpm/ /etc/php/
ADD config/supervisor/ /etc/supervisor/
# ============================================================
# Application Skeleton
# ============================================================
ADD product/ /var/www/html/
# ============================================================
# OCI Config
# ============================================================
# Exposes: 80 (HTTP), 443 (HTTPS), 8080 (WebSocket/Reverb)
# User: www-data
# Entrypoint: frankenphp run --config /etc/caddy/Caddyfile

245
server-php/Dockerfile Normal file
View file

@ -0,0 +1,245 @@
# ============================================================
# Multi-stage Dockerfile for Alpine + Nginx + PHP-FPM
# Supports dynamic PHP versions based on Alpine version
#
# Build targets:
# - builder: Composer install and asset building
# - runtime: Base runtime with PHP and Nginx
# - development: Dev tools (xdebug, phpunit, profiling)
# - production: Hardened production image (default)
# ============================================================
# Build arguments for version control
ARG ALPINE_VERSION=3.22
ARG PHP_VERSION=84
# ============================================================
# Stage 1: Builder - Install dependencies and build assets
# ============================================================
FROM alpine:${ALPINE_VERSION} AS builder
ARG PHP_VERSION
ENV PHP_VERSION=${PHP_VERSION}
# Install build dependencies
RUN apk add --no-cache \
git \
curl \
php${PHP_VERSION} \
php${PHP_VERSION}-phar \
php${PHP_VERSION}-mbstring \
php${PHP_VERSION}-openssl \
php${PHP_VERSION}-curl \
php${PHP_VERSION}-iconv \
php${PHP_VERSION}-tokenizer
# Create php symlink
RUN ln -s /usr/bin/php${PHP_VERSION} /usr/bin/php
# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- \
--install-dir=/usr/bin --filename=composer
WORKDIR /build
# Copy application code
COPY product/ ./
# Install dependencies (only if composer.json exists)
RUN if [ -f composer.json ]; then \
composer install \
--no-dev \
--optimize-autoloader \
--no-interaction \
--no-progress \
--prefer-dist; \
fi
COPY patch/ ./
# ============================================================
# Stage 2: Runtime - Base image with PHP and Nginx
# ============================================================
FROM alpine:${ALPINE_VERSION} AS runtime
ARG PHP_VERSION
ENV PHP_VERSION=${PHP_VERSION}
ENV PHP_INI_DIR=/etc/php${PHP_VERSION}
ENV APP_ENV=production
LABEL maintainer="Snider <snider@host.uk.com>"
LABEL org.opencontainers.image.source="https://github.com/host-uk/docker-server-php"
LABEL org.opencontainers.image.description="Production-ready Alpine+Nginx+PHP-FPM base image"
LABEL org.opencontainers.image.licenses="EUPL-1.2"
LABEL org.opencontainers.image.vendor="Host UK"
LABEL org.opencontainers.image.title="Docker Server PHP"
LABEL org.opencontainers.image.documentation="https://github.com/host-uk/docker-server-php"
# Install only runtime dependencies
RUN apk add --no-cache \
nginx \
nginx-mod-http-brotli \
php${PHP_VERSION} \
php${PHP_VERSION}-bcmath \
php${PHP_VERSION}-ctype \
php${PHP_VERSION}-curl \
php${PHP_VERSION}-dom \
php${PHP_VERSION}-exif \
php${PHP_VERSION}-fileinfo \
php${PHP_VERSION}-fpm \
php${PHP_VERSION}-gd \
php${PHP_VERSION}-iconv \
php${PHP_VERSION}-intl \
php${PHP_VERSION}-mbstring \
php${PHP_VERSION}-mysqli \
php${PHP_VERSION}-opcache \
php${PHP_VERSION}-openssl \
php${PHP_VERSION}-pdo \
php${PHP_VERSION}-pdo_mysql \
php${PHP_VERSION}-phar \
php${PHP_VERSION}-posix \
php${PHP_VERSION}-redis \
php${PHP_VERSION}-session \
php${PHP_VERSION}-simplexml \
php${PHP_VERSION}-sodium \
php${PHP_VERSION}-tokenizer \
php${PHP_VERSION}-xml \
php${PHP_VERSION}-xmlreader \
php${PHP_VERSION}-xmlwriter \
php${PHP_VERSION}-zip \
supervisor \
curl \
ca-certificates \
gettext
# Create php symlink
RUN ln -s /usr/bin/php${PHP_VERSION} /usr/bin/php
WORKDIR /var/www/html
# Copy built application from builder
COPY --chmod=755 --chown=nobody:nobody --from=builder /build /var/www/html
# Copy configuration templates
COPY --chmod=644 --chown=nobody:nobody config/nginx.conf /etc/nginx/nginx.conf
COPY --chmod=755 --chown=nobody:nobody config/conf.d /etc/nginx/conf.d/
COPY --chmod=644 --chown=nobody:nobody config/fpm-pool.conf.template ${PHP_INI_DIR}/php-fpm.d/www.conf.template
COPY --chmod=644 --chown=nobody:nobody config/php.ini.template ${PHP_INI_DIR}/conf.d/custom.ini.template
# Create supervisor directory with proper permissions and copy config
RUN mkdir -p /etc/supervisor/conf.d
COPY --chmod=644 config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# Copy and set up entrypoint
COPY --chmod=755 --chown=nobody:nobody scripts/entrypoint.sh /usr/local/bin/entrypoint.sh
# Set permissions for system directories
RUN chown -R nobody:nobody /run /var/lib/nginx /var/log/nginx ${PHP_INI_DIR}
USER nobody
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl --silent --fail http://127.0.0.1/health || exit 1
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
# ============================================================
# Stage 3: Development - Full dev environment with debugging
# ============================================================
FROM runtime AS development
ARG PHP_VERSION
ENV PHP_VERSION=${PHP_VERSION}
ENV PHP_INI_DIR=/etc/php${PHP_VERSION}
ENV APP_ENV=development
ENV XDEBUG_MODE=develop,debug,coverage
USER root
# Install development tools
RUN apk add --no-cache \
php${PHP_VERSION}-xdebug \
php${PHP_VERSION}-phpdbg \
php${PHP_VERSION}-pecl-pcov \
git \
make \
bash \
vim \
nano
# Install Composer in dev image
RUN curl -sS https://getcomposer.org/installer | php -- \
--install-dir=/usr/bin --filename=composer
# Copy xdebug configuration
COPY --chmod=644 config/xdebug.ini ${PHP_INI_DIR}/conf.d/50_xdebug.ini
# Copy development php.ini overrides
COPY --chmod=644 config/php-dev.ini ${PHP_INI_DIR}/conf.d/60_development.ini
# Install PHPUnit, PHPStan, PHP_CodeSniffer globally
RUN composer global require --no-interaction \
phpunit/phpunit:^11.0 \
phpstan/phpstan:^2.0 \
squizlabs/php_codesniffer:^3.0 \
friendsofphp/php-cs-fixer:^3.0
# Add composer bin to PATH
ENV PATH="/root/.composer/vendor/bin:${PATH}"
# Reset permissions
RUN chown -R nobody:nobody /run /var/lib/nginx /var/log/nginx ${PHP_INI_DIR}
USER nobody
# Override healthcheck for development (more lenient)
HEALTHCHECK --interval=60s --timeout=30s --start-period=10s --retries=5 \
CMD curl --silent --fail http://127.0.0.1/health || exit 1
# ============================================================
# Stage 4: Production - Hardened, optimized production image
# ============================================================
FROM runtime AS production
ARG PHP_VERSION
ENV PHP_VERSION=${PHP_VERSION}
ENV PHP_INI_DIR=/etc/php${PHP_VERSION}
ENV APP_ENV=production
USER root
# Copy production-optimized configurations
COPY --chmod=644 config/opcache-prod.ini ${PHP_INI_DIR}/conf.d/10_opcache_prod.ini
COPY --chmod=644 config/php-prod.ini ${PHP_INI_DIR}/conf.d/60_production.ini
COPY --chmod=644 config/nginx-performance.conf /etc/nginx/conf.d/performance.conf
# Security hardening
RUN set -eux; \
# Remove unnecessary packages
apk del --no-cache \
fortify-headers \
apk-tools 2>/dev/null || true; \
# Remove package cache
rm -rf /var/cache/apk/* /tmp/* /var/tmp/*; \
# Remove shell history
rm -f /root/.ash_history /root/.bash_history 2>/dev/null || true; \
# Set restrictive permissions on sensitive directories
chmod 700 /root 2>/dev/null || true; \
# Remove crontabs
rm -rf /var/spool/cron /etc/crontabs /etc/periodic 2>/dev/null || true; \
# Remove unnecessary user accounts
sed -i -r '/^(nobody|root)/!d' /etc/passwd 2>/dev/null || true; \
sed -i -r '/^(nobody|root)/!d' /etc/shadow 2>/dev/null || true; \
sed -i -r '/^(nobody|root|nogroup)/!d' /etc/group 2>/dev/null || true; \
# Remove interactive shells for system users
sed -i -r 's#^(.*):[^:]*$#\1:/sbin/nologin#' /etc/passwd 2>/dev/null || true
# Reset permissions
RUN chown -R nobody:nobody /run /var/lib/nginx /var/log/nginx ${PHP_INI_DIR}
USER nobody
# Production healthcheck (strict)
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD curl --silent --fail http://127.0.0.1/health || exit 1