Files
cleftlipdata/AGENTS.MD
T
2026-06-06 10:05:39 +07:00

153 lines
4.5 KiB
Markdown

# Docker Deployment Rules
These rules were compiled from real failures during a Laravel + Coolify deployment. Apply them to any Docker/Laravel project.
---
## 1. Pin image tags, never use rolling tags
Don't use `composer:2`, `node:20`, `php:8.2-fpm-alpine` as rolling tags. They change over time and break builds months later. Use the same PHP version as your local environment.
```dockerfile
# BROKEN — will drift
FROM composer:2 AS composer
# GOOD — same PHP version as runtime
FROM php:8.2-cli-alpine AS composer
```
## 2. Verify PHP extensions from composer.lock, not assumptions
Don't guess which extensions are needed. Check what's actually declared:
```bash
grep 'ext-' composer.lock | sort -u
```
The `php:8.2-fpm-alpine` base image already has most extensions compiled in. Only install what's missing (typically just `pdo_mysql`).
```dockerfile
# BROKEN — listing 14 extensions, most already compiled in
RUN docker-php-ext-install pdo_mysql ctype curl dom fileinfo filter hash json libxml mbstring openssl pcre session tokenizer
# GOOD — only what's actually missing
RUN docker-php-ext-install pdo_mysql
```
## 3. Shared services need shared volumes
If Nginx and PHP-FPM are separate containers, Nginx cannot see the app container's files. Use a named volume shared between both:
```yaml
volumes:
app-public: # shared between app and nginx
app-storage: # shared between app and nginx
services:
nginx:
volumes:
- app-public:/var/www/html/public:ro
- app-storage:/var/www/html/storage:ro
app:
volumes:
- app-public:/var/www/html/public
- app-storage:/var/www/html/storage
```
## 4. Never use bash variable interpolation in PHP inline code
Inline PHP in entrypoint scripts must not use `$VARIABLE` inside double-quoted strings. Passwords containing `$`, `\`, or `"` will break bash parsing.
```bash
# BROKEN — password with $, \, " breaks
php -r "new PDO('...', '${DB_PASSWORD}');"
# SAFE — getenv() avoids bash expansion
php -r "new PDO('...', getenv('DB_PASSWORD'));"
```
## 5. Bind mounts fail in Coolify
Coolify resolves compose-relative paths differently from local Docker. Bind-mounting individual config files often fails with "not a directory". Bake configs into a custom image instead.
```yaml
# BROKEN — fails in Coolify
nginx:
image: nginx:alpine
volumes:
- ./docker-nginx.conf:/etc/nginx/conf.d/default.conf:ro
# GOOD — config baked into image
nginx:
build:
dockerfile: Dockerfile.nginx
```
```dockerfile
# Dockerfile.nginx
FROM nginx:1.25-alpine
COPY docker-nginx.conf /etc/nginx/conf.d/default.conf
```
## 6. Don't add Traefik labels to docker-compose in Coolify
Coolify auto-generates Traefik routing labels. Adding them manually causes conflicts or is ignored entirely.
```yaml
# DON'T — Coolify handles this
labels:
- "traefik.enable=true"
- "traefik.http.routers.aigo.rule=Host(`yourdomain.com`)"
- "traefik.http.services.aigo.loadbalancer.server.port=80"
```
## 7. Match the database image to the dump source
Check the SQL dump header to determine the actual database engine and version:
```sql
-- MariaDB dump 10.19 Distrib 10.6.25-MariaDB
-- Server version 10.6.25-MariaDB
```
Use the matching image — MariaDB and MySQL are not interchangeable for dump imports.
```yaml
# Source is MariaDB 10.6
image: mariadb:10.6
# Source is MySQL 8.0
image: mysql:8.0
```
## 8. MariaDB/mysql client needs `--ssl=0` inside containers
The `mariadb-client` package defaults to SSL. MySQL 8.0 and MariaDB containers have self-signed certs, causing connection failures.
```bash
# BROKEN — SSL error with self-signed cert
mysql -h mysql -u user -ppassword dbname < dump.sql
# GOOD — skip SSL for local container connections
mysql -h mysql --ssl=0 -u user -ppassword dbname < dump.sql
```
## 9. Review the full entrypoint flow for restart loops
`set -e` means any command failure exits the script → PHP-FPM never starts → Docker restarts indefinitely. Every step is a potential crash point.
```bash
#!/bin/sh
set -e
# 1. Wait for MySQL — check PDO connects
# 2. Import SQL — check --ssl=0 and credentials
# 3. config:cache — check APP_KEY is set
# 4. storage:link — check permissions
# 5. route:cache, view:cache — check directories are writable
# 6. exec php-fpm — only this should run as the final process
```
## 10. Healtcheck
use mariadb default healthcheck image script if available. if not available ask user first if you want to make a healthcheck for mariadb image.