1 Commits

Author SHA1 Message Date
Frank John Begornia
88912ee8e3 Add Docker and Nginx configuration for Cloud Run deployment 2025-08-12 00:28:17 +08:00
12 changed files with 384 additions and 428 deletions

18
.dockerignore Normal file
View File

@@ -0,0 +1,18 @@
.git
.gitignore
.env
.env.*
.editorconfig
.idea
.vscode
*.md
docker-compose.yml
Dockerfile
vendor/
node_modules/
storage/framework/cache/**
storage/framework/sessions/**
storage/framework/views/**
storage/logs/**
tests/
phpunit.xml

View File

@@ -1,57 +0,0 @@
name: Build and Push Docker Image
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Docker image tag (e.g., v1.0.0, latest)'
required: false
default: 'latest'
push_to_registry:
description: 'Push to registry?'
required: false
default: 'true'
jobs:
build-and-push:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Registry
uses: docker/login-action@v2
with:
registry: ${{ secrets.DOCKER_REGISTRY_URL }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ secrets.DOCKER_REGISTRY_URL }}/merchbay
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY_URL }}/merchbay:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY_URL }}/merchbay:buildcache,mode=max

View File

@@ -1,151 +0,0 @@
name: Deploy Development
on:
push:
branches:
- dev
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
# 1⃣ Checkout code
- name: Checkout code
shell: sh
run: |
git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git /workspace/repo
cd /workspace/repo
git checkout $GITHUB_REF_NAME
# 2⃣ Build image
- name: Build Docker image
shell: sh
run: |
cd /workspace/repo
docker build -t merchbay:dev .
docker save merchbay:dev | gzip > merchbay_dev.tar.gz
# 3⃣ Setup SSH
- name: Setup SSH
shell: sh
env:
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
# 4⃣ Upload artifacts
- name: Upload image and compose
shell: sh
env:
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
run: |
scp -i ~/.ssh/id_ed25519 \
/workspace/repo/merchbay_dev.tar.gz \
/workspace/repo/docker-compose.yml \
${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/
# 5⃣ Deploy on server
- name: Deploy on server
shell: sh
env:
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
run: |
ssh -i ~/.ssh/id_ed25519 $DEPLOY_USER@$DEPLOY_HOST << 'EOF'
set -e
DEPLOY_DIR="/var/www/apps/merchbay_dev"
mkdir -p "$DEPLOY_DIR"
echo "📦 Loading image"
docker load < /tmp/merchbay_dev.tar.gz
echo "📄 Updating compose file"
cp /tmp/docker-compose.yml "$DEPLOY_DIR/"
cd "$DEPLOY_DIR"
echo "🔍 Checking .env file"
if [ ! -f .env ]; then
echo "❌ .env file not found at $DEPLOY_DIR/.env"
echo "Please create it first with required variables:"
echo " - DB_*, PROD_PRIVATE, IMAGES_URL, UPLOAD_URL"
echo " - MAIL_*, CAPTCHA_*, ANALYTICS_*"
exit 1
fi
echo "🔧 Fixing .env permissions"
sudo chown $USER:$USER .env
sudo chmod 600 .env
echo "🌐 Ensure networks"
docker network inspect traefik-public >/dev/null 2>&1 || \
docker network create traefik-public
docker network inspect crew-app-net >/dev/null 2>&1 || \
docker network create crew-app-net
echo "🚀 Starting containers (env vars from .env file)"
docker compose up -d
echo "⏳ Waiting for app container"
sleep 15
if docker ps --format '{{.Names}}' | grep -q merchbay_app; then
echo "🧹 Clearing and rebuilding config cache"
docker compose exec -T app php artisan config:clear
docker compose exec -T app php artisan config:cache
else
echo "❌ App container not running"
docker compose logs
exit 1
fi
echo "🧹 Cleanup"
rm -f /tmp/merchbay_dev.tar.gz /tmp/docker-compose.yml
docker image prune -f
echo "✅ Deployment completed"
EOF
# 6⃣ Health check
- name: Health check
shell: sh
run: |
echo "⏳ Waiting for app to be ready..."
sleep 20
echo "🔍 Testing health check (ignoring SSL cert for now)..."
HTTP_CODE=$(curl -k -s -o /dev/null -w "%{http_code}" --max-time 30 https://dev.merchbay.app || echo "000")
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "301" ]; then
echo "✅ Health check passed! (HTTP $HTTP_CODE)"
echo "⚠️ Note: Using -k to ignore SSL cert. Check Traefik logs if cert not ready."
else
echo "❌ Health check failed! (HTTP $HTTP_CODE)"
echo ""
echo "💡 Troubleshooting:"
echo " 1. Check if container is running:"
echo " docker ps | grep merchbay_app"
echo ""
echo " 2. Check app logs:"
echo " docker logs merchbay_app"
echo ""
echo " 3. Check Traefik logs:"
echo " docker logs traefik"
echo ""
echo " 4. Test manually:"
echo " curl -Ik https://dev.merchbay.app"
exit 1
fi

View File

@@ -1,82 +0,0 @@
name: Deploy Production
on:
push:
branches:
- main
- master
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
shell: sh
run: |
git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git /workspace/repo || true
cd /workspace/repo
git fetch origin $GITHUB_REF_NAME
git checkout $GITHUB_REF_NAME
git pull origin $GITHUB_REF_NAME
- name: Build Docker Image
shell: sh
run: |
cd /workspace/repo
docker build -t merchbay:latest .
docker save merchbay:latest | gzip > merchbay.tar.gz
- name: Setup SSH and Deploy
shell: sh
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "$PROD_DEPLOY_SSH_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keygen -y -f ~/.ssh/deploy_key > /dev/null 2>&1 || { echo "Error: Invalid SSH key format"; exit 1; }
cd /workspace/repo
scp -o StrictHostKeyChecking=no -i ~/.ssh/deploy_key merchbay.tar.gz docker-compose.yml "$PROD_DEPLOY_USER@$PROD_DEPLOY_HOST:/tmp/"
ssh -o StrictHostKeyChecking=no -i ~/.ssh/deploy_key "$PROD_DEPLOY_USER@$PROD_DEPLOY_HOST" "
DEPLOY_DIR='/var/www/merchbay'
mkdir -p \$DEPLOY_DIR
cd /tmp
docker load < merchbay.tar.gz
cp docker-compose.yml \$DEPLOY_DIR/
cd \$DEPLOY_DIR
# .env file should already exist on server with all required variables
# Required: DB_*, PROD_PRIVATE, IMAGES_URL, UPLOAD_URL
# Required: MAIL_*, CAPTCHA_*, ANALYTICS_*
# If it doesn't exist, deployment will fail (this is intentional for security)
docker compose down || true
docker image prune -f
docker network inspect traefik-public >/dev/null 2>&1 || docker network create traefik-public
export DOMAIN=merchbay.app
export APP_URL=https://merchbay.app
docker compose up -d
sleep 10
docker compose exec -T app php artisan migrate --force
docker compose exec -T app php artisan config:cache
docker compose exec -T app php artisan route:cache
docker compose exec -T app php artisan view:cache
rm -f /tmp/merchbay.tar.gz /tmp/docker-compose.yml
echo 'Production deployment completed successfully!'
echo 'Application available at: https://merchbay.app'
"
env:
PROD_DEPLOY_SSH_KEY: ${{ secrets.PROD_DEPLOY_SSH_KEY }}
PROD_DEPLOY_USER: ${{ secrets.PROD_DEPLOY_USER }}
PROD_DEPLOY_HOST: ${{ secrets.PROD_DEPLOY_HOST }}
- name: Health Check
shell: sh
run: |
sleep 10
curl -f https://merchbay.app || exit 1

View File

@@ -1,81 +1,81 @@
# Use PHP 7.3 with Apache (compatible with Laravel 5.0)
FROM php:7.3-apache
# Build stage
FROM php:5.6-fpm-alpine as composer
# Install system dependencies
RUN apt-get update && apt-get install -y \
# Install system dependencies and PHP extensions required for Composer
RUN apk add --no-cache \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
libzip-dev \
zip \
unzip \
libfreetype6-dev \
libjpeg62-turbo-dev \
openssh-client \
libmcrypt \
libmcrypt-dev \
zlib-dev \
libzip-dev \
autoconf \
make \
gcc \
g++ \
&& docker-php-ext-install mcrypt mbstring zip \
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& composer self-update --1
ENV COMPOSER_ALLOW_SUPERUSER=1
WORKDIR /app
COPY composer.* ./
# Install dependencies with Composer (optimize later after full copy)
RUN composer config platform.php 5.6.40 \
&& composer install --prefer-dist --no-dev --no-scripts --no-autoloader
# Copy the rest of the application and optimize autoload
COPY . .
RUN composer dump-autoload --optimize --no-dev --classmap-authoritative
# Production stage
FROM php:5.6-fpm-alpine
# Install runtime dependencies & build PHP extensions
RUN apk add --no-cache \
nginx \
curl \
libpng \
libjpeg-turbo \
freetype \
libzip \
libmcrypt \
zlib \
&& apk add --no-cache --virtual .build-deps \
autoconf make gcc g++ \
libpng-dev libjpeg-turbo-dev freetype-dev libzip-dev libmcrypt-dev zlib-dev \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd
&& docker-php-ext-install -j"$(nproc)" gd pdo pdo_mysql zip mcrypt mbstring opcache \
&& docker-php-ext-enable mcrypt \
&& apk del .build-deps \
&& php -m | grep -i mcrypt
# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath zip tokenizer
# Configure PHP for production
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
&& 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.interned_strings_buffer=8" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.max_accelerated_files=4000" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.revalidate_freq=60" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.fast_shutdown=1" >> /usr/local/etc/php/conf.d/opcache.ini
# Install mcrypt via PECL (required for Laravel 5.0)
RUN apt-get install -y libmcrypt-dev \
&& pecl install mcrypt-1.0.4 \
&& docker-php-ext-enable mcrypt
WORKDIR /var/www
# Enable Apache mod_rewrite
RUN a2enmod rewrite
# Copy vendor & app code from build stage
COPY --from=composer /app/vendor ./vendor
COPY . .
# Install Composer (version 1.x for better compatibility with Laravel 5.0)
COPY --from=composer:1.10 /usr/bin/composer /usr/bin/composer
# Set appropriate permissions and create required directories
RUN chown -R www-data:www-data storage bootstrap \
&& mkdir -p /run/php \
&& chown www-data:www-data /run/php \
&& php artisan key:generate || true
# Set working directory
WORKDIR /var/www/html
# Healthcheck (FPM listens on 9000; adjust as needed if behind nginx)
HEALTHCHECK --interval=30s --timeout=5s CMD php -m > /dev/null || exit 1
# Copy existing application directory contents
COPY . /var/www/html
# Create storage directories and set permissions
RUN mkdir -p storage/framework/views \
storage/framework/cache \
storage/framework/sessions \
storage/logs \
bootstrap/cache
# Set proper ownership and permissions
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 775 /var/www/html/storage \
&& chmod -R 775 /var/www/html/bootstrap/cache
# Create .env file if it doesn't exist
RUN if [ ! -f .env ]; then cp .env.example .env; fi
# Install PHP dependencies (Laravel 5.0 compatible)
RUN composer install --no-dev --no-interaction --prefer-dist
# Generate application key
RUN php artisan key:generate || true
# Run Laravel 5.0 optimization
RUN php artisan clear-compiled && php artisan optimize
# Configure Apache DocumentRoot to point to Laravel's public directory
ENV APACHE_DOCUMENT_ROOT=/var/www/html/public
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
# Suppress Apache ServerName warning
RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
# Copy entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# Expose port 80
EXPOSE 80
# Use entrypoint to set up permissions before starting Apache
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["apache2-foreground"]
EXPOSE 9000
CMD ["php-fpm"]

View File

@@ -0,0 +1,72 @@
# Combined image for Cloud Run: nginx + php-fpm (PHP 5.6) for legacy Laravel 5
# NOTE: PHP 5.6 is EOL; use only for legacy maintenance. Consider upgrading.
FROM php:5.6-fpm
# Set build args/env
ARG APP_ENV=production
ENV APP_ENV=${APP_ENV} \
APP_DEBUG=false \
OPCACHE_VALIDATE_TIMESTAMPS=0 \
COMPOSER_ALLOW_SUPERUSER=1 \
PORT=8080 \
PATH="/var/www/artisan:$PATH"
WORKDIR /var/www
# Install system deps (Debian variant easier than alpine for mixed services)
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
git curl unzip zip supervisor nginx \
libmcrypt4 libmcrypt-dev \
libpng-dev libjpeg62-turbo-dev libfreetype6-dev libzip-dev zlib1g-dev \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install gd mcrypt mbstring pdo pdo_mysql zip opcache \
&& rm -rf /var/lib/apt/lists/*
# Install Composer (v1)
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& composer self-update --1
# Copy composer files & install deps first (cache layer)
COPY composer.* ./
RUN composer config platform.php 5.6.40 \
&& composer install --no-dev --no-scripts --no-autoloader --prefer-dist
# Copy application code
COPY . .
RUN composer dump-autoload --optimize --no-dev --classmap-authoritative || true
# Nginx config
COPY cloudrun/nginx.conf /etc/nginx/nginx.conf
# Supervisord config
COPY cloudrun/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# Remove default nginx site configs if present
RUN rm -f /etc/nginx/sites-enabled/default /etc/nginx/conf.d/default.conf || true
# Create runtime dirs
RUN mkdir -p /run/php /var/log/supervisor /var/www/storage /var/www/bootstrap/cache \
&& chown -R www-data:www-data /var/www/storage /var/www/bootstrap/cache
# PHP production ini tweaks
RUN { \
echo "opcache.enable=1"; \
echo "opcache.memory_consumption=128"; \
echo "opcache.interned_strings_buffer=8"; \
echo "opcache.max_accelerated_files=4000"; \
echo "opcache.revalidate_freq=60"; \
echo "opcache.fast_shutdown=1"; \
echo "date.timezone=UTC"; \
} > /usr/local/etc/php/conf.d/zz-custom.ini
# Generate app key if missing (non-fatal if artisan fails early)
RUN php artisan key:generate || true
# Cloud Run listens on $PORT
EXPOSE 8080
# Health check path suggestion: /healthz (configure in Cloud Run if desired)
# Start supervisor (manages php-fpm + nginx)
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]

42
cloudrun/nginx.conf Normal file
View File

@@ -0,0 +1,42 @@
user nginx;
worker_processes auto;
error_log /dev/stderr warn;
pid /var/run/nginx.pid;
events { worker_connections 1024; }
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /dev/stdout main;
sendfile on;
keepalive_timeout 65;
server_tokens off;
server {
listen 8080 default_server;
listen [::]:8080 default_server;
root /var/www/public;
index index.php index.html;
location /healthz { return 200 'ok'; add_header Content-Type text/plain; }
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000; # php-fpm
fastcgi_index index.php;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
access_log off;
}
}
}

24
cloudrun/supervisord.conf Normal file
View File

@@ -0,0 +1,24 @@
[supervisord]
nodaemon=true
logfile=/dev/stdout
logfile_maxbytes=0
[program:php-fpm]
command=/usr/sbin/php-fpm5.6 -F
priority=10
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;'
priority=20
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

View File

@@ -1,56 +1,74 @@
services:
app:
image: merchbay:dev
container_name: merchbay_app
restart: unless-stopped
environment:
- APP_ENV=${APP_ENV:-production}
- APP_DEBUG=${APP_DEBUG:-false}
- APP_URL=${APP_URL:-http://localhost}
- DB_CONNECTION=mysql
- DB_HOST=${DB_HOST}
- DB_PORT=${DB_PORT:-3306}
- DB_DATABASE=${DB_DATABASE}
- DB_USERNAME=${DB_USERNAME}
- DB_PASSWORD=${DB_PASSWORD}
- PROD_PRIVATE=${PROD_PRIVATE}
- IMAGES_URL=${IMAGES_URL}
- UPLOAD_URL=${UPLOAD_URL}
- MAIL_DRIVER=${MAIL_DRIVER}
- MAIL_HOST=${MAIL_HOST}
- MAIL_PORT=${MAIL_PORT}
- MAIL_USERNAME=${MAIL_USERNAME}
- MAIL_PASSWORD=${MAIL_PASSWORD}
- MAIL_ENCRYPTION=${MAIL_ENCRYPTION}
- CAPTCHA_SITE_KEY=${CAPTCHA_SITE_KEY}
- CAPTCHA_SECRET_KEY=${CAPTCHA_SECRET_KEY}
- ANALYTICS_SITE_ID=${ANALYTICS_SITE_ID}
- ANALYTICS_CLIENT_ID=${ANALYTICS_CLIENT_ID}
- ANALYTICS_SERVICE_EMAIL=${ANALYTICS_SERVICE_EMAIL}
volumes:
- ./storage:/var/www/html/storage
- ./public/uploads:/var/www/html/public/uploads
labels:
- "traefik.enable=true"
- "traefik.http.routers.merchbay-dev.rule=Host(`dev.merchbay.app`)"
- "traefik.http.routers.merchbay-dev.entrypoints=websecure"
- "traefik.http.routers.merchbay-dev.tls=true"
- "traefik.http.routers.merchbay-dev.tls.certresolver=le"
- "traefik.http.services.merchbay-dev.loadbalancer.server.port=80"
# HTTP to HTTPS redirect
- "traefik.http.routers.merchbay-dev-http.rule=Host(`dev.merchbay.app`)"
- "traefik.http.routers.merchbay-dev-http.entrypoints=web"
- "traefik.http.routers.merchbay-dev-http.middlewares=https-redirect"
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
networks:
- traefik-public
- crew-app-net
- default
#PHP Service
app:
build:
context: .
dockerfile: Dockerfile
args:
- APP_ENV=production
image: digitalocean.com/php
container_name: app
restart: unless-stopped
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
environment:
APP_ENV: production
APP_DEBUG: 'false'
PHP_OPCACHE_VALIDATE_TIMESTAMPS: 0
working_dir: /var/www
volumes:
- ./storage:/var/www/storage
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
traefik-public:
external: true
crew-app-net:
external: true
default:
- app-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/"]
interval: 30s
timeout: 3s
retries: 3
#Nginx Service
webserver:
image: nginx:alpine
container_name: webserver
restart: unless-stopped
deploy:
resources:
limits:
cpus: '0.30'
memory: 256M
reservations:
cpus: '0.10'
memory: 128M
ports:
- "10003:80"
- "10443:443"
volumes:
- ./public:/var/www/public
- ./nginx/conf.d/:/etc/nginx/conf.d/
networks:
- app-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/"]
interval: 30s
timeout: 3s
retries: 3
depends_on:
app:
condition: service_healthy
#Docker Networks
networks:
app-network:
driver: bridge
#Volumes
volumes:
dbdata:
driver: local

View File

@@ -1,17 +0,0 @@
#!/bin/bash
set -e
# Create storage directory structure if it doesn't exist
mkdir -p storage/framework/views
mkdir -p storage/framework/cache
mkdir -p storage/framework/sessions
mkdir -p storage/logs
mkdir -p storage/app/public
mkdir -p bootstrap/cache
# Set proper permissions
chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache
# Execute the main command
exec "$@"

View File

@@ -1,9 +1,30 @@
server {
listen 80;
index index.php index.html;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /var/www;
server_tokens off;
# Compression
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
gzip_types
application/javascript
application/json
application/x-javascript
application/xml
text/css
text/javascript
text/plain
text/xml;
client_max_body_size 100M;
fastcgi_read_timeout 1800;
error_log /var/log/nginx/error.log warn;
access_log /var/log/nginx/access.log combined buffer=512k flush=1m;
root /var/www/public;
index index.php index.html;
location / {

68
run.sh Executable file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env bash
set -euo pipefail
# Helper script for docker compose workflow
# Usage examples:
# ./run.sh up # build (if needed) and start in background
# ./run.sh rebuild # force full rebuild (no cache) and start
# ./run.sh down # stop and remove containers
# ./run.sh logs # follow logs
# ./run.sh artisan migrate
# ./run.sh sh # shell into PHP app container
# ./run.sh restart # restart containers
# ./run.sh build # build images (uses cache)
CMD=${1:-up}
shift || true
case "$CMD" in
build)
echo "[build] Building images (with cache)..."
docker compose build
;;
up)
echo "[up] Starting services (build if needed)..."
docker compose up -d --build
;;
rebuild)
echo "[rebuild] Full rebuild without cache..."
docker compose down || true
docker compose build --no-cache
docker compose up -d
;;
down)
echo "[down] Stopping and removing services..."
docker compose down
;;
restart)
echo "[restart] Restarting services..."
docker compose restart
;;
logs)
echo "[logs] Following logs (Ctrl+C to exit)..."
docker compose logs -f --tail=200 "$@"
;;
artisan)
echo "[artisan] php artisan $*"
docker compose exec app php artisan "$@"
;;
sh)
echo "[sh] Opening shell in app container..."
docker compose exec app sh
;;
*)
cat <<EOF
Usage: ./run.sh <command> [args]
Commands:
up Build (if needed) and start containers
build Build images using cache
rebuild Rebuild images without cache then start
down Stop and remove containers
restart Restart running containers
logs Follow logs (pass service names optionally)
artisan Run php artisan <args>
sh Shell into app container
EOF
exit 1
;;
esac