docker setup

This commit is contained in:
Simon Pocrnjič 2026-01-14 17:33:31 +01:00
parent f646b6530a
commit df6c3133ec
16 changed files with 2319 additions and 1 deletions

29
.dockerignore Normal file
View File

@ -0,0 +1,29 @@
.git
.gitignore
.github
.gitattributes
.env
.env.*
!.env.production.example
node_modules
npm-debug.log
vendor
storage/app/*
storage/framework/cache/*
storage/framework/sessions/*
storage/framework/views/*
storage/logs/*
bootstrap/cache/*
public/storage
public/hot
*.md
!README.md
tests
.phpunit.result.cache
phpunit.xml
docker-compose*.yml
.editorconfig
.styleci.yml
*.log
.DS_Store
Thumbs.db

82
.env.local.example Normal file
View File

@ -0,0 +1,82 @@
APP_NAME="Teren App"
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost:8080
APP_LOCALE=sl
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=sl_SI
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
# Database
DB_CONNECTION=pgsql
DB_HOST=postgres
DB_PORT=5432
DB_DATABASE=teren_app
DB_USERNAME=teren_user
DB_PASSWORD=local_password
# Redis
REDIS_CLIENT=phpredis
REDIS_HOST=redis
REDIS_PORT=6379
# Queue
QUEUE_CONNECTION=redis
# Session
SESSION_DRIVER=redis
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=
SESSION_SECURE_COOKIE=false
SESSION_SAME_SITE=lax
# Cache
CACHE_STORE=redis
# Mail (Mailpit for local testing)
MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
SCOUT_PREFIX=
SCOUT_QUEUE=true
# Sanctum
SANCTUM_STATEFUL_DOMAINS=localhost,127.0.0.1,localhost:8080,127.0.0.1:8080
# Logging
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
# Vite
VITE_APP_NAME="${APP_NAME}"
VITE_DEV_SERVER_KEY=
VITE_DEV_SERVER_CERT=
# LibreOffice for document previews (Docker container path)
LIBREOFFICE_BIN=/usr/bin/soffice
# Storage configuration for generated previews
FILES_PREVIEW_DISK=public
FILES_PREVIEW_BASE=previews/casesNEL=null
LOG_LEVEL=debug
# Vite
VITE_DEV_SERVER_KEY=
VITE_DEV_SERVER_CERT=

88
.env.production.example Normal file
View File

@ -0,0 +1,88 @@
APP_NAME="Teren App"
APP_ENV=production
APP_KEY= # Generate with: php artisan key:generate
APP_DEBUG=false
APP_TIMEZONE=UTC
APP_URL=https://example.com # Your domain
APP_LOCALE=sl
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=sl_SI
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
# Database
DB_CONNECTION=pgsql
DB_HOST=postgres
DB_PORT=5432
DB_DATABASE=teren_app
DB_USERNAME=teren_user
DB_PASSWORD= # Generate a strong password
# Redis
REDIS_CLIENT=phpredis
REDIS_HOST=redis
REDIS_PORT=6379
# Queue
QUEUE_CONNECTION=redis
# Session
SESSION_DRIVER=redis
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=
SESSION_SECURE_COOKIE=true
SESSION_SAME_SITE=lax
# Cache
CACHE_STORE=redis
# pgAdmin
PGADMIN_EMAIL=admin@example.com
PGADMIN_PASSWORD= # Generate a strong password
# WireGuard VPN (REQUIRED - app is VPN-only)
WG_SERVERURL=vpn.example.com # Your VPS public IP or domain
WG_UI_PASSWORD= # Generate a strong password for WireGuard dashboard
# Mail (configure as needed)
MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PA
SCOUT_DRIVER=database
SCOUT_PREFIX=
SCOUT_QUEUE=true
# Sanctum
SANCTUM_STATEFUL_DOMAINS=example.com,www.example.com,10.13.13.1
# Logging
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=error
# Vite
VITE_APP_NAME="${APP_NAME}"
# LibreOffice for document previews (Docker container path)
LIBREOFFICE_BIN=/usr/bin/soffice
# Storage configuration for generated previews
FILES_PREVIEW_DISK=public
FILES_PREVIEW_BASE=previews/cases
# Logging
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=error

11
.gitignore vendored
View File

@ -25,7 +25,16 @@ yarn-error.log
check-*.php check-*.php
test-*.php test-*.php
fix-*.php fix-*.php
clean-*.php
mark-*.php
# Development Documentation # Development Documentation
IMPORT_*.md IMPORT_*.md
V2_*.md V2_*.md
REPORTS_*.md
DEDUPLICATION_*.md
# Docker Local Testing
docker-compose.local.yaml
docker-compose.override.yaml
.env.local

1045
DEPLOYMENT_GUIDE.md Normal file

File diff suppressed because it is too large Load Diff

83
Dockerfile Normal file
View File

@ -0,0 +1,83 @@
ARG PHP_VERSION=8.4
FROM php:${PHP_VERSION}-fpm-alpine
# Set working directory
WORKDIR /var/www
# Install system dependencies
RUN apk add --no-cache \
git \
curl \
zip \
unzip \
supervisor \
nginx \
postgresql-dev \
libpng-dev \
libjpeg-turbo-dev \
freetype-dev \
libwebp-dev \
oniguruma-dev \
libxml2-dev \
linux-headers \
${PHPIZE_DEPS}
# Configure and install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
&& docker-php-ext-install -j$(nproc) \
pdo_pgsql \
pgsql \
mbstring \
exif \
pcntl \
bcmath \
gd \
opcache
# Install Redis extension via PECL
RUN pecl install redis \
&& docker-php-ext-enable redis
# Install LibreOffice from community repository
RUN apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community \
libreoffice-common \
libreoffice-writer \
libreoffice-calc
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Create system user to run Composer and Artisan Commands
RUN addgroup -g 1000 -S www && \
adduser -u 1000 -S www -G www
# Copy application files (will be overridden by volume mount in local development)
COPY --chown=www:www . /var/www
# Copy supervisor configuration
COPY docker/supervisor/supervisord.conf /etc/supervisor/supervisord.conf
COPY docker/supervisor/conf.d /etc/supervisor/conf.d
# Set permissions
RUN chown -R www:www /var/www \
&& chmod -R 755 /var/www/storage \
&& chmod -R 755 /var/www/bootstrap/cache
# PHP Configuration for production
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# Copy PHP custom configuration
COPY docker/php/custom.ini $PHP_INI_DIR/conf.d/custom.ini
# Configure PHP-FPM to listen on all interfaces (0.0.0.0) instead of just localhost
# This is needed for nginx running in a separate container to reach PHP-FPM
RUN sed -i 's/listen = 127.0.0.1:9000/listen = 9000/' /usr/local/etc/php-fpm.d/www.conf
# Expose port 9000 for PHP-FPM
EXPOSE 9000
# Create directories for supervisor logs
RUN mkdir -p /var/log/supervisor
# Start supervisor (which will manage both PHP-FPM and Laravel queue workers)
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

343
LOCAL_TESTING_GUIDE.md Normal file
View File

@ -0,0 +1,343 @@
# Local Testing Guide - Windows/Mac/Linux
This guide helps you test the Teren App Docker setup on your local machine without WireGuard VPN.
## Quick Start
### 1. Prerequisites
- Docker Desktop installed and running
- Git
- 8GB RAM recommended
- Ports available: 8080, 5433 (PostgreSQL), 5050, 6379, 9000, 8025, 1025
- **Note:** If you have local PostgreSQL on port 5432, the Docker container uses 5433 instead
### 2. Setup
```bash
# Clone repository (if not already)
git clone YOUR_GITEA_URL
cd Teren-app
# Copy local environment file
cp .env.local.example .env
# Start all services
docker compose -f docker-compose.local.yaml up -d
# Wait for services to start (30 seconds)
timeout 30
# Generate application key
docker compose -f docker-compose.local.yaml exec app php artisan key:generate
# Run migrations
docker compose -f docker-compose.local.yaml exec app php artisan migrate
# Seed database (optional)
docker compose -f docker-compose.local.yaml exec app php artisan db:seed
# Install frontend dependencies (if needed)
npm install
npm run dev
```
### 3. Access Services
| Service | URL | Credentials |
|---------|-----|-------------|
| **Laravel App** | http://localhost:8080 | - |
| **Portainer** | http://localhost:9000 | Set on first visit |
| **pgAdmin** | http://localhost:5050 | admin@local.dev / admin |
| **Mailpit** | http://localhost:8025 | - |
| **PostgreSQL** | localhost:5433 | teren_user / local_password |
| **Redis** | localhost:6379 | - |
**Note:** PostgreSQL uses port 5433 to avoid conflicts with any local PostgreSQL installation.
## Common Commands
### Docker Compose Commands
```bash
# Start all services
docker compose -f docker-compose.local.yaml up -d
# Stop all services
docker compose -f docker-compose.local.yaml down
# View logs
docker compose -f docker-compose.local.yaml logs -f
# View specific service logs
docker compose -f docker-compose.local.yaml logs -f app
# Restart a service
docker compose -f docker-compose.local.yaml restart app
# Rebuild containers
docker compose -f docker-compose.local.yaml up -d --build
# Stop and remove everything (including volumes)
docker compose -f docker-compose.local.yaml down -v
```
### Laravel Commands
```bash
# Run artisan commands
docker compose -f docker-compose.local.yaml exec app php artisan [command]
# Examples:
docker compose -f docker-compose.local.yaml exec app php artisan migrate
docker compose -f docker-compose.local.yaml exec app php artisan db:seed
docker compose -f docker-compose.local.yaml exec app php artisan cache:clear
docker compose -f docker-compose.local.yaml exec app php artisan config:clear
docker compose -f docker-compose.local.yaml exec app php artisan queue:work
# Run tests
docker compose -f docker-compose.local.yaml exec app php artisan test
# Access container shell
docker compose -f docker-compose.local.yaml exec app sh
# Run Composer commands
docker compose -f docker-compose.local.yaml exec app composer install
docker compose -f docker-compose.local.yaml exec app composer update
```
### Database Commands
```bash
# Connect to PostgreSQL (from inside container)
docker compose -f docker-compose.local.yaml exec postgres psql -U teren_user -d teren_app
# Connect from Windows host
psql -h localhost -p 5433 -U teren_user -d teren_app
# Backup database
docker compose -f docker-compose.local.yaml exec postgres pg_dump -U teren_user teren_app > backup.sql
# Restore database
docker compose -f docker-compose.local.yaml exec -T postgres psql -U teren_user teren_app < backup.sql
# Reset database
docker compose -f docker-compose.local.yaml exec app php artisan migrate:fresh --seed
```
## pgAdmin Setup
1. Open http://localhost:5050
2. Login: `admin@local.dev` / `admin`
3. Add Server:
- **General Tab:**
- Name: `Teren Local`
- **Connection Tab:**
- Host: `postgres`
- Port: `5432`
- Database: `teren_app`
- Username: `teren_user`
- Passwo
**External Connection:** To connect from your Windows machine (e.g., DBeaver, pgAdmin desktop), use:
- Host: `localhost`
- Port: `5433` (not 5432)
- Database: `teren_app`
- Username: `teren_user`
- Password: `local_password`rd: `local_password`
4. Click Save
## Mailpit - Email Testing
All emails sent by the application are caught by Mailpit.
- Access: http://localhost:8025
- View all emails in the web interface
- Test email sending:
```bash
docker compose -f docker-compose.local.yaml exec app php artisan tinker
# In tinker:
Mail::raw('Test email', function($msg) {
$msg->to('test@example.com')->subject('Test');
});
```
## Portainer Setup
1. Open http://localhost:9000
2. On first visit, create admin account
3. Select "Docker" environment
4. Click "Connect"
Use Portainer to:
- View and manage containers
- Check logs
- Execute commands in containers
- Monitor resource usage
## Development Workflow
### Frontend Development
The local setup supports live reloading:
```bash
# Run Vite dev server (outside Docker)
npm run dev
# Or inside Docker
docker compose -f docker-compose.local.yaml exec app npm run dev
```
Access: http://localhost:8080
### Code Changes
All code changes are automatically reflected because the source code is mounted as a volume:
```yaml
volumes:
- ./:/var/www # Live code mounting
```
### Queue Workers
Queue workers are running via Supervisor inside the container. To restart:
```bash
# Restart queue workers
docker compose -f docker-compose.local.yaml exec app supervisorctl restart all
# Check status
docker compose -f docker-compose.local.yaml exec app supervisorctl status
# View worker logs
docker compose -f docker-compose.local.yaml exec app tail -f storage/logs/worker.log
```
## Troubleshooting
### Port Already in Use
If you get "port is already allocated" error:
```bash
# Windows - Find process using port
netstat -ano | findstr :8080
# Kill process by PID
taskkill /PID <PID> /F
# Or change port in docker-compose.local.yaml
ports:
- "8081:80" # Change 8080 to 8081
```
### Container Won't Start
```bash
# Check logs
docker compose -f docker-compose.local.yaml logs app
# Rebuild containers
docker compose -f docker-compose.local.yaml down
docker compose -f docker-compose.local.yaml up -d --build
```
### Permission Errors (Linux/Mac)
```bash
# Fix storage permissions
docker compose -f docker-compose.local.yaml exec app chown -R www:www /var/www/storage
docker compose -f docker-compose.local.yaml exec app chmod -R 775 /var/www/storage
```
### Database Connection Failed
```bash
# Check if PostgreSQL is running
docker compose -f docker-compose.local.yaml ps postgres
# Check logs
docker compose -f docker-compose.local.yaml logs postgres
# Restart PostgreSQL
docker compose -f docker-compose.local.yaml restart postgres
```
### Clear All Data and Start Fresh
```bash
# Stop and remove everything
docker compose -f docker-compose.local.yaml down -v
# Remove images
docker compose -f docker-compose.local.yaml down --rmi all
# Start fresh
docker compose -f docker-compose.local.yaml up -d --build
# Re-initialize
docker compose -f docker-compose.local.yaml exec app php artisan key:generate
docker compose -f docker-compose.local.yaml exec app php artisan migrate:fresh --seed
```
## Performance Tips
### Windows Performance
If using WSL2 (recommended):
1. Clone repo inside WSL2 filesystem, not Windows filesystem
2. Use WSL2 terminal for commands
3. Enable WSL2 integration in Docker Desktop settings
### Mac Performance
1. Enable VirtioFS in Docker Desktop settings
2. Disable file watching if not needed
3. Use Docker volumes for vendor directories:
```yaml
volumes:
- ./:/var/www
- /var/www/vendor # Anonymous volume for vendor
- /var/www/node_modules # Anonymous volume for node_modules
```
## Testing Production-Like Setup
To test the production VPN setup locally (advanced):
1. Enable WireGuard in `docker-compose.yaml.example`
2. Change all `10.13.13.1` bindings to `127.0.0.1`
3. Test SSL with self-signed certificates
## Differences from Production
| Feature | Local | Production |
|---------|-------|------------|
| **VPN** | No VPN | WireGuard required |
| **Port** | :8080 | :80/:443 |
| **SSL** | No SSL | Let's Encrypt |
| **Debug** | Enabled | Disabled |
| **Emails** | Mailpit | Real SMTP |
| **Logs** | Debug level | Error level |
| **Code** | Live mount | Built into image |
## Next Steps
After testing locally:
1. Review `docker-compose.yaml.example` for production
2. Follow `DEPLOYMENT_GUIDE.md` for VPS setup
3. Configure WireGuard VPN
4. Deploy to production
## Useful Resources
- [Docker Compose Documentation](https://docs.docker.com/compose/)
- [Laravel Docker Documentation](https://laravel.com/docs/deployment)
- [PostgreSQL Docker](https://hub.docker.com/_/postgres)
- [Mailpit Documentation](https://github.com/axllent/mailpit)

159
QUICK_START_VPN.md Normal file
View File

@ -0,0 +1,159 @@
# Quick Start: VPN-Only Access Setup
⚠️ **IMPORTANT:** This application is configured for VPN-ONLY access. It will NOT be publicly accessible.
## Quick Setup Steps
### 1. Install Docker (on VPS)
```bash
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
```
### 2. Clone & Configure
```bash
git clone YOUR_GITEA_REPO/Teren-app.git
cd Teren-app
cp docker-compose.yaml.example docker-compose.yaml
cp .env.production.example .env
```
### 3. Edit Configuration
```bash
vim .env
```
**Required changes:**
- `WG_SERVERURL` = Your VPS public IP (e.g., `123.45.67.89`)
- `WG_UI_PASSWORD` = Strong password for WireGuard dashboard
- `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD` = Database credentials
- `PGADMIN_EMAIL`, `PGADMIN_PASSWORD` = pgAdmin credentials
### 4. Start WireGuard First
```bash
# Enable kernel module
sudo modprobe wireguard
# Start WireGuard
docker compose up -d wireguard
# Wait 10 seconds
sleep 10
# Check status
docker compose logs wireguard
```
### 5. Setup VPN Client (on your laptop/desktop)
**Access WireGuard Dashboard:** `http://YOUR_VPS_IP:51821`
1. Login with password from step 3
2. Click "New Client"
3. Name it (e.g., "MyLaptop")
4. Download config or scan QR code
**Install WireGuard Client:**
- Windows: https://www.wireguard.com/install/
- macOS: App Store
- Linux: `sudo apt install wireguard`
- Mobile: App Store / Play Store
**Import config and CONNECT**
### 6. Verify VPN Works
```bash
# From your local machine (while connected to VPN)
ping 10.13.13.1
```
Should get responses ✅
### 7. Secure WireGuard Dashboard
Edit `docker-compose.yaml`:
```yaml
# Find wireguard service, change:
ports:
- "51821:51821/tcp"
# To:
ports:
- "10.13.13.1:51821:51821/tcp"
```
```bash
docker compose down
docker compose up -d wireguard
```
### 8. Start All Services
```bash
# Make sure you're connected to VPN!
docker compose up -d
```
### 9. Initialize Application
```bash
# Generate app key
docker compose exec app php artisan key:generate
# Run migrations
docker compose exec app php artisan migrate --force
# Cache config
docker compose exec app php artisan config:cache
```
### 10. Access Your Services
**While connected to VPN:**
| Service | URL |
|---------|-----|
| **Laravel App** | http://10.13.13.1 |
| **Portainer** | http://10.13.13.1:9000 |
| **pgAdmin** | http://10.13.13.1:5050 |
| **WireGuard UI** | http://10.13.13.1:51821 |
## Firewall Configuration
```bash
sudo ufw allow 22/tcp # SSH
sudo ufw allow 51820/udp # WireGuard VPN
sudo ufw enable
```
**That's it!** ✅
## Adding More VPN Clients
1. Connect to VPN
2. Open: `http://10.13.13.1:51821`
3. Click "New Client"
4. Download config
5. Import on new device
## Troubleshooting
**Can't connect to VPN:**
```bash
docker compose logs wireguard
sudo ufw status
```
**Can't access app after VPN connection:**
```bash
ping 10.13.13.1
docker compose ps
docker compose logs nginx
```
**Check which ports are exposed:**
```bash
docker compose ps
sudo netstat -tulpn | grep 10.13.13.1
```
## Full Documentation
See `DEPLOYMENT_GUIDE.md` for complete setup instructions, SSL configuration, automated deployments, and troubleshooting.

74
deploy.sh Normal file
View File

@ -0,0 +1,74 @@
#!/bin/bash
# Teren App Deployment Script
# This script handles automated deployment from Gitea
set -e # Exit on any error
echo "🚀 Starting deployment..."
# Configuration
PROJECT_DIR="/var/www/Teren-app"
BRANCH="main" # Change to your production branch
GITEA_REPO="git@your-gitea-server.com:username/Teren-app.git"
# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# Change to project directory
cd $PROJECT_DIR
echo "📥 Pulling latest changes from $BRANCH..."
git fetch origin $BRANCH
git reset --hard origin/$BRANCH
echo "🔧 Copying production environment file..."
if [ ! -f .env ]; then
echo "${RED}❌ .env file not found! Please create it from .env.production.example${NC}"
exit 1
fi
echo "🐳 Building and starting Docker containers..."
docker-compose down
docker-compose build --no-cache app
docker-compose up -d
echo "⏳ Waiting for containers to be healthy..."
sleep 10
echo "📦 Installing/updating Composer dependencies..."
docker-compose exec -T app composer install --no-dev --optimize-autoloader --no-interaction
echo "🎨 Building frontend assets..."
# If you build assets locally or in CI/CD, uncomment:
# npm ci
# npm run build
echo "🔑 Optimizing Laravel..."
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
docker-compose exec -T app php artisan event:cache
echo "📊 Running database migrations..."
docker-compose exec -T app php artisan migrate --force
echo "🗄️ Clearing old caches..."
docker-compose exec -T app php artisan cache:clear
docker-compose exec -T app php artisan queue:restart
echo "🔄 Restarting queue workers..."
docker-compose restart app
echo "${GREEN}✅ Deployment completed successfully!${NC}"
# Optional: Send notification (Slack, Discord, etc.)
# curl -X POST -H 'Content-type: application/json' \
# --data '{"text":"🚀 Teren App deployed successfully!"}' \
# YOUR_WEBHOOK_URL
# Show running containers
echo "📋 Running containers:"
docker-compose ps

189
docker-compose.yaml.example Normal file
View File

@ -0,0 +1,189 @@
version: '3.8'
services:
# Laravel Application
app:
build:
context: .
dockerfile: Dockerfile
args:
- PHP_VERSION=8.4
container_name: teren-app
restart: unless-stopped
working_dir: /var/www
volumes:
- ./:/var/www
- ./storage:/var/www/storage
- ./bootstrap/cache:/var/www/bootstrap/cache
environment:
- APP_ENV=${APP_ENV:-production}
- APP_DEBUG=${APP_DEBUG:-false}
- DB_CONNECTION=pgsql
- DB_HOST=postgres
- DB_PORT=5432
- DB_DATABASE=${DB_DATABASE}
- DB_USERNAME=${DB_USERNAME}
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
- REDIS_PORT=6379
- QUEUE_CONNECTION=redis
- LIBREOFFICE_BIN=/usr/bin/soffice
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- teren-network
# Supervisor runs inside the container (defined in Dockerfile)
# Includes PHP-FPM, Laravel queue workers, and queue-sms workers
# Nginx Web Server (VPN-only access)
nginx:
image: nginx:alpine
container_name: teren-nginx
restart: unless-stopped
ports:
- "10.13.13.1:80:80" # Only accessible via WireGuard VPN
- "10.13.13.1:443:443" # Only accessible via WireGuard VPN
volumes:
- ./:/var/www
- ./docker/nginx/conf.d:/etc/nginx/conf.d
- ./docker/nginx/ssl:/etc/nginx/ssl
- ./docker/certbot/conf:/etc/letsencrypt
- ./docker/certbot/www:/var/www/certbot
depends_on:
- app
networks:
- teren-network
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
# Certbot for SSL certificates
certbot:
image: certbot/certbot
container_name: teren-certbot
restart: unless-stopped
volumes:
- ./docker/certbot/conf:/etc/letsencrypt
- ./docker/certbot/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
networks:
- teren-network
# PostgreSQL Database
postgres:
image: postgres:16-alpine
container_name: teren-postgres
restart: unless-stopped
ports:
- "127.0.0.1:5432:5432" # Only accessible via localhost (or VPN)
environment:
- POSTGRES_DB=${DB_DATABASE}
- POSTGRES_USER=${DB_USERNAME}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- PGDATA=/var/lib/postgresql/data/pgdata
volumes:
- postgres-data:/var/lib/postgresql/data
- ./docker/postgres/init:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- teren-network
# pgAdmin - PostgreSQL UI
pgadmin:
image: dpage/pgadmin4:latest
container_name: teren-pgadmin
restart: unless-stopped
ports:
- "127.0.0.1:5050:80" # Only accessible via localhost (or VPN)
environment:
- PGADMIN_DEFAULT_EMAIL=${PGADMIN_EMAIL:-admin@admin.com}
- PGADMIN_DEFAULT_PASSWORD=${PGADMIN_PASSWORD:-admin}
- PGADMIN_CONFIG_SERVER_MODE=True
- PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED=True
volumes:
- pgadmin-data:/var/lib/pgadmin
depends_on:
- postgres
networks:
- teren-network
# Redis for caching and queues
redis:
image: redis:7-alpine
container_name: teren-redis
restart: unless-stopped
ports:
- "127.0.0.1:6379:6379"
volumes:
- redis-data:/data
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
networks:
- teren-network
# WireGuard VPN with Web UI Dashboard
wireguard:
image: weejewel/wg-easy:latest
container_name: teren-wireguard
restart: unless-stopped
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- WG_HOST=${WG_SERVERURL} # Your VPS public IP or domain
- PASSWORD=${WG_UI_PASSWORD} # Password for WireGuard UI
- WG_PORT=51820
- WG_DEFAULT_ADDRESS=10.13.13.x
- WG_DEFAULT_DNS=1.1.1.1,1.0.0.1
- WG_MTU=1420
- WG_PERSISTENT_KEEPALIVE=25
- WG_ALLOWED_IPS=10.13.13.0/24
volumes:
- wireguard-data:/etc/wireguard
ports:
- "51820:51820/udp" # WireGuard VPN port (public)
- "51821:51821/tcp" # WireGuard Web UI (public for initial setup, then VPN-only)
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv4.ip_forward=1
networks:
- teren-network
# Portainer - Docker Management UI (VPN-only access)
portainer:
image: portainer/portainer-ce:latest
container_name: teren-portainer
restart: unless-stopped
ports:
- "10.13.13.1:9000:9000" # Portainer UI (VPN-only)
- "10.13.13.1:9443:9443" # Portainer HTTPS (VPN-only)
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer-data:/data
networks:
- teren-network
networks:
teren-network:
driver: bridge
volumes:
postgres-data:
driver: local
pgadmin-data:
driver: local
redis-data:
driver: local
wireguard-data:
driver: local
portainer-data:
driver: local

View File

@ -0,0 +1,86 @@
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com; # Change this to your domain
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com; # Change this to your domain
root /var/www/public;
index index.php index.html;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # Change this
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # Change this
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Laravel location configuration
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_hide_header X-Powered-By;
# Increase timeouts for long-running requests
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
}
location ~ /\.(?!well-known).* {
deny all;
}
# Deny access to sensitive files
location ~ /\.env {
deny all;
}
# Cache static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
gzip_disable "msie6";
client_max_body_size 100M;
}

View File

@ -0,0 +1,53 @@
server {
listen 80;
server_name localhost;
root /var/www/public;
index index.php index.html;
# Logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Laravel location configuration
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_hide_header X-Powered-By;
# Increase timeouts for debugging
fastcgi_read_timeout 3600;
fastcgi_send_timeout 3600;
}
location ~ /\.(?!well-known).* {
deny all;
}
# Deny access to sensitive files
location ~ /\.env {
deny all;
}
# Cache static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
gzip_disable "msie6";
client_max_body_size 100M;
}

23
docker/php/custom.ini Normal file
View File

@ -0,0 +1,23 @@
; PHP Custom Configuration for Production
upload_max_filesize = 100M
post_max_size = 100M
memory_limit = 512M
max_execution_time = 300
max_input_time = 300
; OPcache settings
opcache.enable = 1
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0
opcache.save_comments = 1
opcache.fast_shutdown = 1
; Production settings
expose_php = Off
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php_errors.log

View File

@ -0,0 +1,25 @@
[program:laravel-queue]
process_name=%(program_name)s_%(process_num)02d
command=/usr/local/bin/php /var/www/artisan queue:work --sleep=3 --tries=3 --timeout=300 --verbose
autostart=true
autorestart=true
user=www
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/storage/logs/worker.log
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=10
stopwaitsecs=360
[program:laravel-queue-sms]
process_name=%(program_name)s_%(process_num)02d
command=/usr/local/bin/php /var/www/artisan queue:work --queue=sms --sleep=3 --tries=3 --timeout=90 --verbose
autostart=true
autorestart=true
user=www
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/storage/logs/worker-sms.log
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=10
stopwaitsecs=360

View File

@ -0,0 +1,11 @@
[program:php-fpm]
command=/usr/local/sbin/php-fpm --nodaemonize --fpm-config /usr/local/etc/php-fpm.d/www.conf
autostart=true
autorestart=true
priority=5
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
stdout_events_enabled=true
stderr_events_enabled=true

View File

@ -0,0 +1,19 @@
[unix_http_server]
file=/var/run/supervisor.sock
chmod=0700
[supervisord]
nodaemon=true
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
childlogdir=/var/log/supervisor
user=root
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock
[include]
files = /etc/supervisor/conf.d/*.conf