Add docker production files

This commit is contained in:
myrmidex 2025-08-02 03:22:09 +02:00
parent 0f041983dd
commit ace0db0446
5 changed files with 343 additions and 0 deletions

View file

@ -0,0 +1,85 @@
# Multi-stage build for FFR Laravel application
FROM node:20 AS frontend-builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install Node dependencies
RUN npm ci
# Copy frontend source
COPY resources/ resources/
COPY public/ public/
COPY vite.config.js ./
COPY tsconfig.json ./
# Build frontend assets
RUN npm run build
# PHP runtime stage
FROM php:8.4-fpm-alpine
# Install system dependencies
RUN apk add --no-cache \
git \
curl \
libpng-dev \
libxml2-dev \
zip \
unzip \
oniguruma-dev \
mysql-client \
nginx \
supervisor \
autoconf \
gcc \
g++ \
make
# Install PHP extensions
RUN docker-php-ext-install \
pdo_mysql \
mbstring \
exif \
pcntl \
bcmath \
gd \
&& pecl install redis \
&& docker-php-ext-enable redis
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Set working directory
WORKDIR /var/www/html
# Copy application code first
COPY . .
# Install PHP dependencies after copying all files
RUN composer install --no-dev --optimize-autoloader --no-interaction
# Copy built frontend assets from builder stage
COPY --from=frontend-builder /app/public/build/ ./public/build/
# Copy nginx and supervisor configurations
COPY docker/production/nginx.conf /etc/nginx/http.d/default.conf
COPY docker/production/supervisord.conf /etc/supervisord.conf
COPY docker/production/start-app.sh /usr/local/bin/start-app
# Set proper permissions
RUN chown -R www-data:www-data storage bootstrap/cache public/build \
&& chmod -R 755 storage bootstrap/cache \
&& chmod +x /usr/local/bin/start-app
# Expose port 80 for nginx
EXPOSE 80
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD php artisan --version || exit 1
# Start the application
CMD ["/usr/local/bin/start-app"]

View file

@ -0,0 +1,130 @@
services:
app:
image: codeberg.org/lvl0/ffr:latest
# build:
# context: ../..
# dockerfile: docker/production/Dockerfile
container_name: ffr-app
restart: unless-stopped
working_dir: /var/www/html
environment:
- APP_ENV=production
- APP_DEBUG=false
- DB_CONNECTION=mysql
- DB_HOST=db
- DB_PORT=3306
- DB_DATABASE=ffr
- DB_USERNAME=ffr_user
- DB_PASSWORD=ffr_password
- REDIS_HOST=redis
- REDIS_PORT=6379
- CACHE_DRIVER=redis
- SESSION_DRIVER=redis
- QUEUE_CONNECTION=redis
volumes: []
ports:
- "8000:80"
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- ffr-network
queue:
image: codeberg.org/lvl0/ffr:latest
container_name: ffr-queue
restart: unless-stopped
working_dir: /var/www/html
environment:
- APP_ENV=production
- APP_DEBUG=false
- DB_CONNECTION=mysql
- DB_HOST=db
- DB_PORT=3306
- DB_DATABASE=ffr
- DB_USERNAME=ffr_user
- DB_PASSWORD=ffr_password
- REDIS_HOST=redis
- REDIS_PORT=6379
- CACHE_DRIVER=redis
- SESSION_DRIVER=redis
- QUEUE_CONNECTION=redis
command: ["php", "artisan", "horizon"]
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- ffr-network
scheduler:
image: codeberg.org/lvl0/ffr:latest
container_name: ffr-scheduler
restart: unless-stopped
working_dir: /var/www/html
environment:
- APP_ENV=production
- APP_DEBUG=false
- DB_CONNECTION=mysql
- DB_HOST=db
- DB_PORT=3306
- DB_DATABASE=ffr
- DB_USERNAME=ffr_user
- DB_PASSWORD=ffr_password
- REDIS_HOST=redis
- REDIS_PORT=6379
- CACHE_DRIVER=redis
- SESSION_DRIVER=redis
- QUEUE_CONNECTION=redis
command: ["sh", "-c", "while true; do php artisan schedule:run --verbose --no-interaction; sleep 60; done"]
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- ffr-network
db:
image: docker.io/library/mysql:8.0
container_name: ffr-db
restart: unless-stopped
environment:
- MYSQL_DATABASE=ffr
- MYSQL_USER=ffr_user
- MYSQL_PASSWORD=ffr_password
- MYSQL_ROOT_PASSWORD=root_password
volumes:
- db_data:/var/lib/mysql
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "ffr_user", "-pffr_password"]
timeout: 5s
retries: 5
interval: 3s
start_period: 30s
networks:
- ffr-network
redis:
image: docker.io/library/redis:7-alpine
container_name: ffr-redis
restart: unless-stopped
volumes:
- redis_data:/data
networks:
- ffr-network
networks:
ffr-network:
driver: bridge
volumes:
db_data:
driver: local
redis_data:
driver: local

View file

@ -0,0 +1,69 @@
server {
listen 80;
server_name localhost;
root /var/www/html/public;
index index.php index.html;
# Security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
# Increase timeouts for long-running requests
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
}
# Deny access to hidden files
location ~ /\.ht {
deny all;
}
# Deny access to sensitive files
location ~ /\.(env|git) {
deny all;
}
# Static assets with far-future expiry
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|map)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Laravel specific optimizations
location = /favicon.ico {
access_log off;
log_not_found off;
}
location = /robots.txt {
access_log off;
log_not_found off;
}
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/xml+rss
application/json;
}

View file

@ -0,0 +1,34 @@
#!/bin/sh
# Create .env file if it doesn't exist
if [ ! -f /var/www/html/.env ]; then
cp /var/www/html/.env.example /var/www/html/.env 2>/dev/null || touch /var/www/html/.env
fi
# Wait for database to be ready
echo "Waiting for database..."
while ! mysql -h db -u ffr_user -pffr_password --connect-timeout=2 -e "SELECT 1" >/dev/null 2>&1; do
echo "Database not ready, waiting..."
sleep 1
done
echo "Database connection established!"
# Generate app key if not set
if ! grep -q "APP_KEY=base64:" /var/www/html/.env; then
php artisan key:generate --force
fi
# Laravel optimizations for production
php artisan config:cache
php artisan route:cache
php artisan view:cache
# Run migrations
php artisan migrate --force
# Run seeders (only if needed for production data)
php artisan db:seed --force --class=PlatformInstanceSeeder
php artisan db:seed --force --class=SettingsSeeder
# Start supervisor to manage nginx and php-fpm
supervisord -c /etc/supervisord.conf

View file

@ -0,0 +1,25 @@
[supervisord]
nodaemon=true
user=root
logfile=/var/log/supervisord.log
pidfile=/var/run/supervisord.pid
[program:nginx]
command=nginx -g "daemon off;"
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
priority=10
[program:php-fpm]
command=php-fpm --nodaemonize
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
priority=10