Set up docker for local and production

This commit is contained in:
myrmidex 2025-09-26 01:13:44 +02:00
parent dac8fb4f2c
commit a2f5a112d3
21 changed files with 426 additions and 0 deletions

41
.env.local Normal file
View file

@ -0,0 +1,41 @@
# Application
APP_NAME="Trip Planner"
APP_ENV=local
APP_KEY=base64:0YqH2qbjRZYGieiv8EiDVGUqEKLxT5Zwmv3nY9S75cc=
APP_DEBUG=true
APP_URL=http://localhost:8000
# Database
DB_CONNECTION=mysql
DB_HOST=database
DB_PORT=3306
DB_DATABASE=trip_planner
DB_USERNAME=trip_user
DB_PASSWORD=secret
# Redis
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
# Cache & Session
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
# Mail (using Mailpit for local development)
MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@tripplanner.local"
MAIL_FROM_NAME="${APP_NAME}"
# Frontend URL for CORS
FRONTEND_URL=http://localhost:5173
# API Settings
API_PREFIX=api
SANCTUM_STATEFUL_DOMAINS=localhost:5173

0
backend/bootstrap/cache/.gitignore vendored Normal file → Executable file
View file

0
backend/storage/app/.gitignore vendored Normal file → Executable file
View file

0
backend/storage/app/private/.gitignore vendored Normal file → Executable file
View file

0
backend/storage/app/public/.gitignore vendored Normal file → Executable file
View file

0
backend/storage/framework/.gitignore vendored Normal file → Executable file
View file

0
backend/storage/framework/cache/.gitignore vendored Normal file → Executable file
View file

0
backend/storage/framework/cache/data/.gitignore vendored Normal file → Executable file
View file

0
backend/storage/framework/sessions/.gitignore vendored Normal file → Executable file
View file

0
backend/storage/framework/testing/.gitignore vendored Normal file → Executable file
View file

0
backend/storage/framework/views/.gitignore vendored Normal file → Executable file
View file

0
backend/storage/logs/.gitignore vendored Normal file → Executable file
View file

79
docker-compose.dev.yml Normal file
View file

@ -0,0 +1,79 @@
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: ../docker/frontend/Dockerfile.dev
container_name: trip-planner-frontend-dev
ports:
- "5173:5173"
volumes:
- ./frontend:/app:Z
- node_modules:/app/node_modules
environment:
- NODE_ENV=development
networks:
- trip-planner-network
backend:
build:
context: ./backend
dockerfile: ../docker/backend/Dockerfile.dev
container_name: trip-planner-backend-dev
ports:
- "8000:8000"
volumes:
- ./backend:/var/www/html:Z
- vendor:/var/www/html/vendor
env_file:
- .env.local
depends_on:
- database
- redis
networks:
- trip-planner-network
database:
image: docker.io/library/mariadb:11
container_name: trip-planner-db-dev
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-secret}
MYSQL_DATABASE: ${DB_DATABASE:-trip_planner}
MYSQL_USER: ${DB_USERNAME:-trip_user}
MYSQL_PASSWORD: ${DB_PASSWORD:-secret}
volumes:
- db-data:/var/lib/mysql
networks:
- trip-planner-network
redis:
image: docker.io/library/redis:alpine
container_name: trip-planner-redis-dev
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- trip-planner-network
mailpit:
image: docker.io/axllent/mailpit:latest
container_name: trip-planner-mailpit-dev
ports:
- "1025:1025"
- "8025:8025"
networks:
- trip-planner-network
networks:
trip-planner-network:
driver: bridge
volumes:
db-data:
redis-data:
node_modules:
vendor:

58
docker-compose.prod.yml Normal file
View file

@ -0,0 +1,58 @@
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: ../docker/frontend/Dockerfile.prod
container_name: trip-planner-frontend
ports:
- "${FRONTEND_PORT:-80}:80"
restart: unless-stopped
networks:
- trip-planner-network
backend:
build:
context: ./backend
dockerfile: ../docker/backend/Dockerfile.prod
container_name: trip-planner-backend
ports:
- "${BACKEND_PORT:-8080}:80"
environment:
APP_ENV: production
APP_DEBUG: false
APP_URL: ${APP_URL}
DB_CONNECTION: mysql
DB_HOST: ${DB_HOST}
DB_PORT: ${DB_PORT:-3306}
DB_DATABASE: ${DB_DATABASE}
DB_USERNAME: ${DB_USERNAME}
DB_PASSWORD: ${DB_PASSWORD}
REDIS_HOST: redis
REDIS_PORT: 6379
CACHE_DRIVER: redis
QUEUE_CONNECTION: redis
SESSION_DRIVER: redis
depends_on:
- redis
restart: unless-stopped
networks:
- trip-planner-network
redis:
image: docker.io/library/redis:alpine
container_name: trip-planner-redis
volumes:
- redis-data:/data
command: redis-server --appendonly yes
restart: unless-stopped
networks:
- trip-planner-network
networks:
trip-planner-network:
driver: bridge
volumes:
redis-data:

View file

@ -0,0 +1,43 @@
FROM php:8.3-fpm-alpine
# Install system dependencies
RUN apk add --no-cache \
git \
curl \
libpng-dev \
oniguruma-dev \
libxml2-dev \
zip \
unzip \
nodejs \
npm \
shadow
# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
# Create developer user with UID 1000 (same as host user)
RUN adduser -u 1000 -s /bin/sh -D developer
# Create storage and bootstrap/cache directories
RUN mkdir -p storage/app/public storage/framework/cache storage/framework/sessions storage/framework/views storage/logs bootstrap/cache
# Change ownership to developer user
RUN chown -R developer:developer /var/www/html
# Set proper permissions for Laravel directories
RUN chmod -R 775 storage bootstrap/cache
# Switch to developer user
USER developer
# Expose port 8000 for artisan serve
EXPOSE 8000
# Start Laravel development server with composer install
CMD sh -c "composer install && php artisan key:generate --force && php artisan serve --host=0.0.0.0 --port=8000"

View file

@ -0,0 +1,69 @@
# Build stage
FROM php:8.3-fpm-alpine AS builder
# Install system dependencies
RUN apk add --no-cache \
libpng-dev \
oniguruma-dev \
libxml2-dev \
zip \
unzip
# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd opcache
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
# Copy composer files
COPY composer.json composer.lock ./
# Install dependencies (no dev)
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist
# Copy application
COPY . .
# Generate optimized autoloader
RUN composer dump-autoload --optimize --classmap-authoritative
# Production stage
FROM php:8.3-fpm-alpine
# Install runtime dependencies
RUN apk add --no-cache \
libpng-dev \
oniguruma-dev \
libxml2-dev \
nginx \
supervisor
# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd opcache
WORKDIR /var/www/html
# Copy application from builder
COPY --from=builder /var/www/html .
# Copy nginx config
COPY docker/nginx/backend.conf /etc/nginx/http.d/default.conf
# Copy supervisord config
COPY docker/backend/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# Configure PHP-FPM
RUN echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.memory_consumption=128" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.max_accelerated_files=10000" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.validate_timestamps=0" >> /usr/local/etc/php/conf.d/opcache.ini
# Set permissions
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
# Expose port 80
EXPOSE 80
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]

View file

@ -0,0 +1,21 @@
[supervisord]
nodaemon=true
user=root
[program:php-fpm]
command=/usr/local/sbin/php-fpm -F
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

View file

@ -0,0 +1,18 @@
FROM node:20-alpine
# Install global dependencies as root
RUN npm install -g vite
WORKDIR /app
# Change ownership of /app to node user (UID 1000)
RUN chown -R node:node /app
# Switch to node user (UID 1000, same as host user)
USER node
# Expose Vite dev server port
EXPOSE 5173
# Start development server
CMD ["sh", "-c", "npm install && npm run dev -- --host 0.0.0.0"]

View file

@ -0,0 +1,30 @@
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy app files
COPY . .
# Build the app
RUN npm run build
# Production stage
FROM nginx:alpine
# Copy built app from builder stage
COPY --from=builder /app/dist /usr/share/nginx/html
# Copy nginx config
COPY docker/nginx/frontend.conf /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

40
docker/nginx/backend.conf Normal file
View file

@ -0,0 +1,40 @@
server {
listen 80;
server_name localhost;
root /var/www/html/public;
index index.php;
# Disable access log for performance
access_log off;
error_log /var/log/nginx/error.log error;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml application/atom+xml image/svg+xml text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffer_size 32k;
fastcgi_buffers 8 16k;
fastcgi_read_timeout 240;
}
location ~ /\.(?!well-known).* {
deny all;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}

View file

@ -0,0 +1,27 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml application/atom+xml image/svg+xml text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype;
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}