Skip to content

Commit 77c6135

Browse files
authored
Add Dockerfile for frankenphp (#3889)
1 parent a772a9e commit 77c6135

File tree

13 files changed

+882
-290
lines changed

13 files changed

+882
-290
lines changed

.dockerignore

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Git
2+
.git
3+
.gitignore
4+
.gitattributes
5+
6+
# CI/CD
7+
.github
8+
9+
# Coding
10+
.ai
11+
.vscode
12+
13+
# dependencies
14+
composer-cache
15+
16+
# Documentation
17+
README.md
18+
docs/
19+
20+
# Development files
21+
.editorconfig
22+
.php-cs-fixer.php
23+
.php-cs-fixer.cache
24+
phpstan.neon
25+
phpunit.xml
26+
.phpunit.result.cache
27+
phpstan/*
28+
scripts/*
29+
vite/*
30+
31+
# Node
32+
node_modules/
33+
npm-debug.log
34+
35+
# Lychee
36+
/public/uploads/*
37+
lychee/*
38+
39+
# Laravel
40+
/storage/logs/*
41+
/storage/framework/cache/*
42+
/storage/framework/sessions/*
43+
/storage/framework/views/*
44+
.env
45+
.env.*
46+
!.env.example
47+
48+
# IDE
49+
.vscode/
50+
.idea/
51+
*.swp
52+
*.swo
53+
*~
54+
55+
# OS
56+
.DS_Store
57+
Thumbs.db
58+
59+
# Tests
60+
tests/*

.github/workflows/CICD.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,101 @@ jobs:
148148
- code_style_errors
149149
uses: ./.github/workflows/php_dist.yml
150150

151+
docker_check:
152+
name: 3️⃣ Dockerfile Lint
153+
runs-on: ubuntu-latest
154+
needs:
155+
- phpstan
156+
- check_js
157+
158+
steps:
159+
- name: Harden Runner
160+
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
161+
with:
162+
egress-policy: audit
163+
164+
- name: Checkout code
165+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
166+
167+
- name: Set up Docker Buildx
168+
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f #v3.12.0
169+
170+
- name: Docker Lint
171+
uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5 # v3.3.0
172+
with:
173+
dockerfile: ./Dockerfile
174+
failure-threshold: warning
175+
176+
- name: Build Docker image locally
177+
run: docker build -t lychee:local .
178+
179+
- name: Run Trivy vulnerability scanner
180+
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # v0.33.1
181+
with:
182+
image-ref: lychee:local
183+
format: 'table'
184+
exit-code: 1
185+
ignore-unfixed: true
186+
vuln-type: 'os,library'
187+
severity: 'CRITICAL,HIGH'
188+
189+
docker-build:
190+
name: 4️⃣ Build Docker Image
191+
runs-on: ubuntu-latest
192+
if: >
193+
(github.ref == 'refs/heads/master' && github.event_name == 'push') ||
194+
(startsWith(github.ref, 'refs/tags/') && github.event_name == 'push')
195+
needs:
196+
- docker_check
197+
permissions:
198+
contents: read
199+
packages: write
200+
201+
steps:
202+
- name: Harden Runner
203+
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
204+
with:
205+
egress-policy: audit
206+
207+
- name: Checkout code
208+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
209+
210+
- name: Set up Docker Buildx
211+
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f #v3.12.0
212+
213+
- name: Log in to GitHub Container Registry
214+
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
215+
with:
216+
registry: ghcr.io
217+
username: ${{ github.actor }}
218+
password: ${{ secrets.GITHUB_TOKEN }}
219+
220+
- name: Extract metadata (tags, labels)
221+
id: meta
222+
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
223+
with:
224+
images: ghcr.io/${{ github.repository }}
225+
flavor: |
226+
latest=${{ startsWith(github.ref, 'refs/tags/') }}
227+
tags: |
228+
# define default branch
229+
type=edge,branch=master
230+
# branch event
231+
type=ref,event=branch
232+
# tag event
233+
type=ref,event=tag
234+
235+
- name: Build and push Docker image
236+
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
237+
with:
238+
push: true
239+
tags: ${{ steps.meta.outputs.tags }}
240+
labels: ${{ steps.meta.outputs.labels }}
241+
cache-from: type=gha
242+
cache-to: type=gha,mode=max
243+
build-args: |
244+
NODE_ENV=production
245+
151246
createArtifact:
152247
name: 3️⃣ Build Artifact
153248
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')

Dockerfile

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Lychee - Laravel Backend Dockerfile
2+
# Multi-stage build with Laravel Octane + FrankenPHP
3+
ARG NODE_ENV=production
4+
5+
# ============================================================================
6+
# Stage 1: Composer Dependencies
7+
# ============================================================================
8+
FROM composer:2.8@sha256:5248900ab8b5f7f880c2d62180e40960cd87f60149ec9a1abfd62ac72a02577c AS composer
9+
10+
WORKDIR /app
11+
12+
# Copy composer files first for layer caching
13+
COPY composer.json composer.lock ./
14+
15+
# Install dependencies (no dev packages for production)
16+
RUN composer install \
17+
--no-dev \
18+
--no-interaction \
19+
--no-progress \
20+
--no-scripts \
21+
--prefer-dist \
22+
--optimize-autoloader \
23+
--ignore-platform-reqs
24+
25+
# ============================================================================
26+
# Stage 2: Node.js Build for Frontend Assets
27+
# ============================================================================
28+
FROM node:20-alpine@sha256:658d0f63e501824d6c23e06d4bb95c71e7d704537c9d9272f488ac03a370d448 AS node
29+
30+
# Build argument to control dev vs production build
31+
ARG NODE_ENV
32+
ENV NODE_ENV=$NODE_ENV
33+
34+
WORKDIR /app
35+
36+
# Copy package files for layer caching
37+
COPY package.json package-lock.json ./
38+
39+
# Install dependencies
40+
RUN npm ci --no-audit
41+
42+
# Copy frontend source
43+
COPY resources/ ./resources/
44+
COPY public/ ./public/
45+
COPY lang/ ./lang/
46+
COPY vite.config.ts vite.embed.config.ts tsconfig.json ./
47+
48+
# Build frontend assets
49+
# When NODE_ENV=development, Vite sets import.meta.env.DEV=true
50+
RUN npm run build
51+
52+
# ============================================================================
53+
# Stage 3: Production FrankenPHP Image
54+
# ============================================================================
55+
FROM dunglas/frankenphp:php8.4-alpine@sha256:49654aea8f2b9bc225bde6d89c9011054505ca2ed3e9874b251035128518b491
56+
57+
ARG USER=appuser
58+
59+
LABEL maintainer="lycheeorg"
60+
LABEL org.opencontainers.image.source="https://github.com/LycheeOrg/Lychee"
61+
62+
# Install system utilities and PHP extensions
63+
# hadolint ignore=DL3018
64+
RUN apk add --no-cache \
65+
exiftool \
66+
shadow \
67+
ffmpeg \
68+
gd \
69+
grep \
70+
imagemagick \
71+
jpegoptim \
72+
netcat-openbsd \
73+
unzip \
74+
curl \
75+
&& install-php-extensions \
76+
pdo_mysql \
77+
pdo_pgsql \
78+
gd \
79+
zip \
80+
dom \
81+
bcmath \
82+
sodium \
83+
opcache \
84+
pcntl \
85+
exif \
86+
imagick \
87+
intl \
88+
redis \
89+
tokenizer \
90+
&& rm -rf /var/cache/apk/* \
91+
&& apk del shadow
92+
93+
WORKDIR /app
94+
95+
# Copy application code
96+
COPY --chown=www-data:www-data . .
97+
98+
# Copy vendor from composer stage
99+
COPY --from=composer --chown=www-data:www-data /app/vendor ./vendor
100+
101+
# Copy built frontend assets from node stage
102+
COPY --from=node --chown=www-data:www-data /app/public/build ./public/build
103+
104+
# Ensure storage and bootstrap/cache are writable with minimal permissions
105+
RUN mkdir -p storage/framework/cache \
106+
storage/framework/sessions \
107+
storage/framework/views \
108+
storage/logs \
109+
bootstrap/cache \
110+
public/dist \
111+
&& chown -R www-data:www-data storage bootstrap/cache public/dist \
112+
&& chmod -R 750 storage bootstrap/cache \
113+
&& chmod -R 755 public/dist \
114+
&& touch /app/frankenphp_target \
115+
&& touch /app/public/dist/user.css \
116+
&& touch /app/public/dist/custom.js \
117+
&& chown www-data:www-data /app/public/dist/user.css /app/public/dist/custom.js \
118+
&& chmod 644 /app/public/dist/user.css /app/public/dist/custom.js \
119+
&& cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini \
120+
&& echo "upload_max_filesize=110M" > $PHP_INI_DIR/conf.d/custom.ini \
121+
&& echo "post_max_size=110M" >> $PHP_INI_DIR/conf.d/custom.ini \
122+
&& echo "max_execution_time=3000" >> $PHP_INI_DIR/conf.d/custom.ini \
123+
&& echo "expose_php=Off" >> $PHP_INI_DIR/conf.d/custom.ini \
124+
&& echo "display_errors=Off" >> $PHP_INI_DIR/conf.d/custom.ini \
125+
&& echo "log_errors=On" >> $PHP_INI_DIR/conf.d/custom.ini
126+
127+
# Copy entrypoint and validation scripts
128+
COPY docker/scripts/entrypoint.sh /usr/local/bin/entrypoint.sh
129+
COPY docker/scripts/validate-env.sh /usr/local/bin/validate-env.sh
130+
COPY docker/scripts/create-admin-user.sh /usr/local/bin/create-admin-user.sh
131+
COPY docker/scripts/permissions-check.sh /usr/local/bin/permissions-check.sh
132+
RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/validate-env.sh /usr/local/bin/permissions-check.sh /usr/local/bin/create-admin-user.sh
133+
134+
# Expose port 8000 (Octane)
135+
EXPOSE 8000
136+
137+
# Set entrypoint
138+
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
139+
140+
# Default command: run Octane with FrankenPHP
141+
CMD ["php", "artisan", "octane:start", "--server=frankenphp", "--host=0.0.0.0", "--port=8000"]

0 commit comments

Comments
 (0)