October CMS sur OxPHP
October CMS (basé sur Laravel) est livré avec un index.php à la racine du projet, à côté de config/, .env et vendor/. Sa configuration nginx réécrit tout vers index.php et ne sert les fichiers statiques que depuis des chemins d'assets sur liste d'autorisation (*/assets). Pour reproduire cette posture sur OxPHP sans exposer les sources des thèmes ou la configuration, cette recette construit une racine de documents public/ avec php artisan october:mirror public et fait pointer le mode framework dessus.
C'est la recette qui met à l'épreuve SYMLINK_ALLOW_PATHS d'OxPHP.
La stack en un coup d'œil
- Image OxPHP :
ghcr.io/oxphp/oxphp:0.13.0-php8.4-alpine3.23(PHP 8.4) - Mode de routage : Framework (
ENTRY_FILE=index.php;DOCUMENT_ROOTlaissé à la valeur par défaut/var/www/html/public) - Extensions ajoutées :
gd,pdo_mysql,mbstring,zip - Services : OxPHP + MySQL
- URL :
http://localhost:8098· backend/admin· internehttp://localhost:9099/health
october/october s'installe directement depuis Packagist. La stack de composants Laravel 11 est antérieure à PHP 8.5, cette recette épingle donc l'image PHP 8.4.
Arborescence du projet
mkdir -p october-oxphp/src
docker run --rm -e COMPOSER_ALLOW_SUPERUSER=1 \
-v "$PWD/october-oxphp/src":/app -w /app \
composer:2 create-project october/october . --no-interaction --prefer-distFaites ensuite pointer src/.env vers le service de base de données (DB_HOST=db, DB_DATABASE=october, …) et définissez APP_URL=http://localhost:8098.
Dockerfile et fichier compose
Le Dockerfile est nommé src/Dockerfile.oxphp pour éviter toute collision avec les assets propres à October.
ARG OXPHP_VERSION=0.13.0
ARG PHP_VERSION=8.4
ARG ALPINE_VERSION=3.23
# ── PHP base: October runtime extensions ──────────────────────
FROM php:${PHP_VERSION}-zts-alpine${ALPINE_VERSION} AS php-base
RUN apk add --no-cache \
libpng libjpeg-turbo freetype oniguruma libzip \
&& apk add --no-cache --virtual .build-deps \
libpng-dev libjpeg-turbo-dev freetype-dev oniguruma-dev libzip-dev \
&& docker-php-ext-configure gd --with-jpeg --with-freetype \
&& docker-php-ext-install -j"$(nproc)" gd pdo_mysql mbstring zip \
&& apk del .build-deps
# ── Composer ──────────────────────────────────────────────────
FROM composer:2 AS composer
# ── OxPHP artifacts (PHP 8.4 image) ───────────────────────────
FROM ghcr.io/oxphp/oxphp:${OXPHP_VERSION}-php${PHP_VERSION}-alpine${ALPINE_VERSION} AS oxphp
# ── dev: OxPHP server + PHP CLI + Composer ────────────────────
FROM php-base AS dev
RUN apk add --no-cache libgcc git
COPY --from=composer /usr/bin/composer /usr/local/bin/composer
COPY --from=oxphp /usr/local/bin/oxphp /usr/local/bin/oxphp
COPY --from=oxphp /usr/local/lib/liboxphp_bridge.so /usr/local/lib/
COPY --from=oxphp /usr/local/lib/php/extensions/ /tmp/oxphp-ext/
RUN cp /tmp/oxphp-ext/*/oxphp_sapi.so "$(php -r 'echo ini_get("extension_dir");')/" \
&& rm -rf /tmp/oxphp-ext \
&& echo "extension=oxphp_sapi.so" > /usr/local/etc/php/conf.d/oxphp-ext.ini
RUN { \
echo "[opcache]"; echo "opcache.enable=1"; echo "opcache.enable_cli=1"; \
echo "opcache.validate_timestamps=1"; echo "opcache.revalidate_freq=0"; \
} > /usr/local/etc/php/conf.d/opcache-dev.ini
RUN { \
echo "memory_limit=256M"; echo "upload_max_filesize=64M"; \
echo "post_max_size=64M"; echo "max_execution_time=300"; \
} > /usr/local/etc/php/conf.d/october.ini
RUN getent passwd www-data >/dev/null \
|| adduser -D -H -u 82 -G www-data -s /sbin/nologin www-data
RUN mkdir -p /var/www/html/public && chown -R www-data:www-data /var/www/html
ENV LD_LIBRARY_PATH=/usr/local/lib
WORKDIR /var/www/html
EXPOSE 80 9090
CMD ["oxphp"]services:
app:
build:
context: ./src
dockerfile: Dockerfile.oxphp
target: dev
image: october-oxphp/app:dev
container_name: october-oxphp
ports:
- "8098:80"
- "9099:9090"
volumes:
- ./src:/var/www/html
environment:
# LISTEN_ADDR (0.0.0.0:80) and DOCUMENT_ROOT (/var/www/html/public) are
# the OxPHP defaults, so both are omitted.
ENTRY_FILE: index.php # framework mode
INTERNAL_ADDR: 0.0.0.0:9090
ACCESS_LOG: all
# The public/ mirror is built from symlinks into the project tree;
# allow OxPHP to follow them out of the document root.
SYMLINK_ALLOW_PATHS: /var/www/html
depends_on:
db:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://0.0.0.0:9090/health"]
interval: 10s
timeout: 3s
retries: 5
start_period: 5s
db:
image: mysql:8.0
container_name: october-oxphp-db
ports:
- "3313:3306"
command:
- --max_allowed_packet=64M
environment:
MYSQL_DATABASE: october
MYSQL_USER: october
MYSQL_PASSWORD: october
MYSQL_ROOT_PASSWORD: root
volumes:
- db_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-proot"]
interval: 5s
timeout: 5s
retries: 30
start_period: 15s
restart: unless-stopped
volumes:
db_data:Installation et premier lancement
October CMS 4 n'a pas de commande d'assistant october:install et ne provisionne aucun administrateur par défaut ; la séquence est donc : migrer → construire les tables de contenu → créer le miroir → créer l'administrateur :
docker compose up -d db
# 1. Schema and module seeds
docker compose run --rm app php artisan october:migrate
# 2. Dynamic content tables (the demo theme uses Tailor content)
docker compose run --rm app php artisan tailor:migrate
# 3. Build the public/ document root (index.php + */assets symlinks)
docker compose run --rm app sh -c 'mkdir -p public && php artisan october:mirror public --relative'
# 4. Create a backend super-user (no install wizard exists)
docker compose run --rm app php artisan tinker --execute '
$u = Backend\Models\User::firstOrNew(["login" => "admin"]);
$u->email = "admin@example.com"; $u->password = "admin123";
$u->password_confirmation = "admin123"; $u->is_superuser = true;
$u->is_activated = true; $u->save();'
docker compose up -d appNotes OxPHP
october:mirror publicreproduit la liste blanche d'assets nginx d'October. Elle crée un répertoirepublic/ne contenant queindex.php(un lien symbolique) et des liens symboliques vers les répertoires*/assets— les pages de thème.htm,config/,.env,vendor/etstorage/logsrestent en dehors de la racine de documents. AvecDOCUMENT_ROOT=…/public, aucun d'entre eux n'est accessible via HTTP.SYMLINK_ALLOW_PATHS=/var/www/htmlest obligatoire. OxPHP bloque par défaut les liens symboliques qui pointent en dehors deDOCUMENT_ROOT. Les liens symboliques du miroir renvoient dans l'arborescence du projet (/var/www/html/modules/…,/var/www/html/themes/…), c'est donc la racine du projet qui est mise sur liste d'autorisation./varfigure sur une liste de refus exacte mais pas sur une liste de refus par préfixe, donc/var/www/htmlest autorisé. Voir Symlink Allow Paths.tailor:migrateest facile à oublier. Sans elle, le thème de démonstration renvoieSQLSTATE 1146: table 'xc_…' doesn't exist, car ses collections de contenu sont des blueprints Tailor dont les tables sont construites séparément deoctober:migrate.
Vérifier
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8098/ # 200
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8098/admin # 302 → login
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8098/themes/demo/assets/css/theme.css # 200
# the document-root mirror keeps internals out:
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8098/.env # 404
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8098/themes/demo/pages/home.htm # 404Prompts pour votre agent
Ces prompts supposent un agent connecté au serveur MCP de la documentation OxPHP — il lira lui-même les pages dont il a besoin.
Fais tourner mon projet October CMS sur OxPHP en local. Lis la page `examples/cms/october` via le serveur MCP de la documentation OxPHP.
Écris le Dockerfile et le fichier Compose pour ce dépôt, puis donne-moi la séquence d'installation dans l'ordre : migrer, Tailor compris, construire le miroir `public/` à partir de mes thèmes et plugins, et créer un administrateur backend.Mon site October CMS sur OxPHP sert les pages mais les assets de mes thèmes et plugins renvoient 404. Lis la page `examples/cms/october` via le serveur MCP de la documentation OxPHP.
Confronte mon miroir `public/` aux répertoires `*/assets` que mes thèmes et plugins livrent réellement, et à `SYMLINK_ALLOW_PATHS` dans mon fichier Compose, et dis-moi lequel des deux est faux.Voir aussi
- Routage · Symlink Allow Paths · Dot-Path Blocking
- Drupal et Craft CMS — les autres recettes de CMS en mode framework