forked from m3ue/m3u-editor
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
306 lines (265 loc) · 11 KB
/
Dockerfile
File metadata and controls
306 lines (265 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# syntax=docker/dockerfile:1
########################################
# Build Arguments - Configurable at build time
########################################
# Allow customization of m3u-proxy repository and branch
# NOTE: GitHub Actions workflow automatically overrides these with dynamic values
# based on the repository owner (e.g., m3ue/m3u-proxy for m3ue/m3u-editor)
# Default: upstream m3ue/m3u-proxy (main branch) - used for manual builds
# Override: --build-arg M3U_PROXY_REPO=https://github.com/yourusername/m3u-proxy.git
# --build-arg M3U_PROXY_BRANCH=dev
ARG M3U_PROXY_REPO=https://github.com/m3ue/m3u-proxy.git
ARG M3U_PROXY_BRANCH=master
ARG INSTALL_DEV_DEPENDENCIES=false
ARG INSTALL_CLAMAV=false
# Optional: use a local m3u-proxy directory instead of cloning from git.
# Must be a path relative to the Docker build context.
# Example (monorepo): docker build -f m3u-editor/Dockerfile .. \
# --build-arg M3U_PROXY_LOCAL_DIR=m3u-proxy
# Example (compose): build: { context: .., args: { M3U_PROXY_LOCAL_DIR: m3u-proxy } }
ARG M3U_PROXY_LOCAL_DIR=""
########################################
# Stage 1: Composer builder - installs PHP dependencies
########################################
FROM composer:2 AS composer_builder
WORKDIR /app
ARG INSTALL_DEV_DEPENDENCIES=false
# Copy composer metadata first for better layer caching
COPY composer.json composer.lock ./
# Install dependencies first (cached if composer files unchanged)
# Some platform requirements (ext-intl, ext-pcntl) are provided by the runtime image
RUN if [ "${INSTALL_DEV_DEPENDENCIES}" = "true" ]; then \
composer install --no-interaction --no-progress -o --prefer-dist --ignore-platform-reqs --no-scripts --no-autoloader; \
else \
composer install --no-dev --no-interaction --no-progress -o --prefer-dist --ignore-platform-reqs --no-scripts --no-autoloader; \
fi
# Copy application code for autoload generation
COPY app/ ./app/
COPY bootstrap/ ./bootstrap/
COPY config/ ./config/
COPY database/ ./database/
COPY routes/ ./routes/
COPY artisan ./
# Generate optimized autoloader
RUN if [ "${INSTALL_DEV_DEPENDENCIES}" = "true" ]; then \
composer dump-autoload --optimize; \
else \
composer dump-autoload --no-dev --optimize --classmap-authoritative; \
fi
########################################
# Stage 2: Node builder - builds frontend assets
########################################
FROM node:22-alpine AS node_builder
WORKDIR /app
# Copy package files first for better layer caching
# Docker will automatically invalidate this layer when package*.json files change
COPY package.json package-lock.json ./
# Install all dependencies including dev deps (Vite is needed for build)
# Note: NODE_ENV must NOT be set to production here, or npm ci will skip devDependencies
RUN npm ci --silent
# Copy only files needed for the build
COPY vite.config.js postcss.config.js ./
COPY resources/ ./resources/
COPY public/ ./public/
# Copy app/ so Tailwind v4 can scan @source '../../app/Filament' for used classes
COPY app/ ./app/
# Copy vendor built by composer stage for Vite to resolve vendor CSS
COPY --from=composer_builder /app/vendor ./vendor
# Run the frontend build (Vite) with production optimizations
# NODE_ENV=production enables minification and tree-shaking
RUN NODE_ENV=production npm run build && \
# Clean up node_modules after build - not needed in final image
rm -rf node_modules
########################################
# Stage 3: m3u-proxy builder - prepares Python proxy service
########################################
FROM alpine:3.21.3 AS proxy_builder
# Cache bust arg - when this changes, Docker invalidates the layer cache
# Pass the latest m3u-proxy commit SHA to ensure fresh clones
ARG M3U_PROXY_COMMIT=""
# Re-declare ARGs for this stage
ARG M3U_PROXY_REPO=https://github.com/m3ue/m3u-proxy.git
ARG M3U_PROXY_BRANCH=master
ARG M3U_PROXY_LOCAL_DIR=""
WORKDIR /opt/m3u-proxy
# Install git for cloning (only needed when M3U_PROXY_LOCAL_DIR is not set)
RUN apk add --no-cache git
# Use a local directory from the build context if M3U_PROXY_LOCAL_DIR is set,
# otherwise clone from git. The --mount=type=bind makes the entire build context
# available at /build-context without copying files into the image layer.
RUN --mount=type=bind,target=/build-context \
if [ -n "${M3U_PROXY_LOCAL_DIR}" ]; then \
echo "Using local m3u-proxy source from build context: ${M3U_PROXY_LOCAL_DIR}" && \
cp -r "/build-context/${M3U_PROXY_LOCAL_DIR}/." . ; \
else \
echo "Cloning m3u-proxy from: ${M3U_PROXY_REPO} (branch: ${M3U_PROXY_BRANCH}, commit: ${M3U_PROXY_COMMIT})" && \
git clone -b ${M3U_PROXY_BRANCH} ${M3U_PROXY_REPO} . ; \
fi && \
rm -rf .git
########################################
# Stage 4: Runtime image
########################################
FROM alpine:3.21.3 AS runtime
# Labels for image metadata
LABEL org.opencontainers.image.title="m3u-editor" \
org.opencontainers.image.description="M3U Editor - IPTV playlist management" \
org.opencontainers.image.vendor="sparkison" \
org.opencontainers.image.licenses="MIT"
WORKDIR /var/www/html
# Re-declare ARGs in this stage so they're available
ARG GIT_BRANCH
ARG GIT_COMMIT
ARG GIT_TAG
ARG INSTALL_CLAMAV=false
# Set environment variables
ENV GIT_BRANCH=${GIT_BRANCH} \
GIT_COMMIT=${GIT_COMMIT} \
GIT_TAG=${GIT_TAG} \
WWWGROUP="m3ue" \
WWWUSER="m3ue"
# Add Alpine edge repositories and install ALL system packages in a single layer
# This maximizes layer caching and reduces image size
RUN echo "@edge https://dl-cdn.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories && \
echo "@edge https://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories && \
apk update && apk add --no-cache \
# Core utilities
coreutils \
openssl \
supervisor \
envsubst \
su-exec \
nano \
wget \
curl \
curl-dev \
sqlite \
ca-certificates \
bash \
tzdata \
# Node.js runtime (for Reverb/websockets if needed)
nodejs \
npm \
# Redis server
redis \
# FFmpeg 8.0 from Alpine edge (with matching edge deps to avoid symbol mismatches)
# glslang-libs, spirv-tools, and vulkan-loader must also come from edge
# to match the ABI that ffmpeg@edge was built against.
ffmpeg@edge \
glslang-libs@edge \
spirv-tools@edge \
vulkan-loader@edge \
# Nginx web server
nginx \
# PostgreSQL server & client
postgresql \
postgresql-client \
postgresql-contrib \
# Python runtime and pip (for m3u-proxy)
python3 \
py3-pip \
# PHP 8.4 and all required extensions
php84-cli \
php84-fpm \
php84-posix \
php84-openssl \
php84-sqlite3 \
php84-gd \
php84-curl \
php84-intl \
php84-imap \
php84-mbstring \
php84-xml \
php84-zip \
php84-bcmath \
php84-soap \
php84-xmlreader \
php84-xmlwriter \
php84-iconv \
php84-ldap \
php84-tokenizer \
php84-msgpack \
php84-opcache \
php84-pdo_sqlite \
php84-pdo_pgsql \
php84-phar \
php84-fileinfo \
php84-pecl-igbinary \
php84-pecl-imagick \
php84-pecl-redis \
php84-pcntl && \
if [ "${INSTALL_CLAMAV}" = "true" ]; then \
apk add --no-cache \
clamav \
clamav-libunrar \
freshclam && \
if [ -f /etc/clamav/freshclam.conf ]; then \
sed -i 's/^\s*NotifyClamd/# NotifyClamd/' /etc/clamav/freshclam.conf; \
fi; \
fi && \
# Create PHP symlink
ln -s /usr/bin/php84 /usr/bin/php && \
# Clean up apk cache
rm -rf /var/cache/apk/*
# Create user and group early for proper file ownership
RUN addgroup ${WWWGROUP} && \
adduser -h /var/www/html -s /bin/bash -G ${WWWGROUP} -D ${WWWUSER}
# Setup cron for Laravel scheduler
RUN echo '* * * * * cd /var/www/html && /usr/bin/php artisan schedule:run >> /dev/null 2>&1' | crontab -
# Create required directories with proper ownership
RUN mkdir -p \
/etc/supervisor.d/conf.d \
/var/log/supervisor \
/var/lib/postgresql \
/run/postgresql && \
touch /var/run/supervisord.pid \
/var/log/supervisor/supervisord.log && \
chown -R ${WWWUSER}:${WWWGROUP} \
/var/lib/nginx \
/var/lib/postgresql \
/run/postgresql
# Copy configuration files (these change less frequently)
COPY --chown=${WWWUSER}:${WWWGROUP} ./docker/8.4/redis.conf /etc/redis/redis.tmpl
COPY --chown=root:root ./docker/8.4/php.ini /etc/php84/conf.d/99-m3ue.ini
COPY --chown=root:root ./docker/8.4/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY --chown=root:root ./docker/8.4/nginx/nginx.conf /etc/nginx/nginx.tmpl
COPY --chown=root:root ./docker/8.4/nginx/laravel.conf /etc/nginx/conf.d/laravel.tmpl
COPY --chown=root:root ./docker/8.4/nginx/xtream.conf /etc/nginx/conf.d/xtream.tmpl
COPY --chown=root:root ./docker/8.4/www.conf /etc/php84/php-fpm.d/www.tmpl
# Copy container startup script
COPY --chmod=755 start-container /usr/local/bin/start-container
COPY --chmod=755 docker/run-tests /usr/local/bin/run-tests
# Copy m3u-proxy from builder stage
COPY --from=proxy_builder --chown=${WWWUSER}:${WWWGROUP} /opt/m3u-proxy /opt/m3u-proxy
# Install m3u-proxy Python dependencies
# Using --break-system-packages since we control the container and don't need isolation
RUN if [ -f /opt/m3u-proxy/requirements.txt ]; then \
pip3 install --no-cache-dir --break-system-packages -r /opt/m3u-proxy/requirements.txt; \
fi
# Copy application code (changes more frequently)
COPY --chown=${WWWUSER}:${WWWGROUP} . /var/www/html
# Create git info file
RUN echo "GIT_BRANCH=${GIT_BRANCH}" > /var/www/html/.git-info && \
echo "GIT_COMMIT=${GIT_COMMIT}" >> /var/www/html/.git-info && \
echo "GIT_TAG=${GIT_TAG}" >> /var/www/html/.git-info && \
echo "BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> /var/www/html/.git-info
# Copy build artifacts from builder stages (overwrite source files)
# Vendor directory from composer builder
COPY --from=composer_builder --chown=${WWWUSER}:${WWWGROUP} /app/vendor /var/www/html/vendor
# Built frontend assets from node builder
COPY --from=node_builder --chown=${WWWUSER}:${WWWGROUP} /app/public/build /var/www/html/public/build
# Create artisan command alias
RUN echo -e '#!/bin/bash\nphp artisan app:"$@"' > /usr/bin/m3ue && \
chmod +x /usr/bin/m3ue
# Ensure proper permissions for storage and cache directories
RUN chown -R ${WWWUSER}:${WWWGROUP} /var/www/html && \
chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache 2>/dev/null || true
# Note: Ports are configured via environment variables (APP_PORT, REVERB_PORT, etc.)
# and should be exposed in docker-compose.yml or via -p flags as needed.
# Default ports: APP_PORT=36400, REVERB_PORT=36800, M3U_PROXY_PORT=8085, XTREAM_PORT=36401
# Health check for the application
# APP_PORT is set at runtime (default 36400); use the same default here so the
# Dockerfile HEALTHCHECK works even when docker-compose is not in use.
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost:${APP_PORT:-36400}/up || exit 1
# Final entrypoint
ENTRYPOINT ["start-container"]