Teren-app/DEPLOYMENT_GUIDE.md
Simon Pocrnjič df6c3133ec docker setup
2026-01-14 17:33:31 +01:00

1046 lines
22 KiB
Markdown

# Teren App - Ubuntu 24.04 VPS Deployment Guide
This guide covers the complete setup of the Teren App on Ubuntu 24.04 using Docker, including SSL, WireGuard VPN, and automated deployments.
## Table of Contents
1. [Prerequisites](#prerequisites)
2. [Initial VPS Setup](#initial-vps-setup)
3. [Docker Installation](#docker-installation)
4. [WireGuard VPN Setup](#wireguard-vpn-setup) ⚠️ **SETUP FIRST - APP IS VPN-ONLY**
5. [Application Setup](#application-setup)
6. [SSL Certificate Setup](#ssl-certificate-setup)
7. [Portainer Setup](#portainer-setup)
8. [Automated Deployment Setup](#automated-deployment-setup)
9. [Monitoring & Maintenance](#monitoring--maintenance)
10. [Troubleshooting](#troubleshooting)
---
## Prerequisites
- Ubuntu 24.04 VPS with root access
- Domain name pointed to your VPS IP (optional - can use IP only)
- Self-hosted Gitea server
- At least 2GB RAM (4GB recommended)
- 20GB+ storage
**IMPORTANT:** This application is configured to be accessible ONLY through WireGuard VPN. The app will NOT be publicly accessible.
---
## Initial VPS Setup
### 1. Update System
```bash
sudo apt update && sudo apt upgrade -y
```
### 2. Create Deploy User
```bash
# Create user
sudo adduser deployer
sudo usermod -aG sudo deployer
# Switch to deploy user
su - deployer
```
### 3. Configure Firewall
```bash
# Install UFW
sudo apt install ufw -y
# Allow SSH, HTTP, HTTPS, and WireGuard
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 51820/udp # WireGuard
# Enable firewall
sudo ufw enable
sudo ufw status
```
### 4. Install Essential Tools
```bash
sudo apt install -y \
git \
curl \
wget \
unzip \
vim \
htop \
software-properties-common
```
---
## Docker Installation
### 1. Install Docker
```bash
# Add Docker's official GPG key
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```
### 2. Configure Docker
```bash
# Add user to docker group
sudo usermod -aG docker $USER
# Enable Docker service
sudo systemctl enable docker
sudo systemctl start docker
# Verify installation
docker --version
docker compose version
# Log out and back in for group changes to take effect
exit
su - deployer
```
---WireGuard VPN Setup
⚠️ **CRITICAL: Setup WireGuard FIRST before the application!** The app is configured to be VPN-only and will not be publicly accessible.
### 1. Enable Kernel Modules
```bash
# Load WireGuard kernel module
sudo apt install -y wireguard-tools
sudo modprobe wireguard
# Make it persistent
echo "wireguard" | sudo tee -a /etc/modules
```
### 2. Configure Environment
```bash
cd /var/www/Teren-app
# Edit .env file
vim .env
```
Set these WireGuard variables:
- `WG_SERVERURL`: Your VPS public IP or domain (e.g., `vpn.example.com` or `192.168.1.100`)
- `WG_UI_PASSWORD`: Strong password for WireGuard dashboard
### 3. Start WireGuard Container
```bash
# Start only WireGuard first
docker compose up -d wireguard
# Wait for it to initialize
sleep 10
# Check status
docker compose logs wireguard
```
### 4. Access WireGuard Dashboard
The WireGuard Web UI is temporarily accessible publicly for initial setup:
**URL:** `http://YOUR_VPS_IP:51821`
Login with the password you set in `WG_UI_PASSWORD`.
### 5. Create Your First Client
1. In the WireGuard dashboard, click **"New Client"**
2. Give it a name (e.g., "MyLaptop")
3. Click the QR code or download the config file
4. Install WireGuard on your device:
- **Windows:** https://www.wireguard.com/install/
- **macOS:** App Store
- **Linux:** `sudo apt install wireguard`
- **iOS/Android:** App Store / Play Store
5. ImpoStart Application Containers
**Note:** Make sure you're connected to the WireGuard VPN before proceeding.
```bash
# Start all application containers
docker compose up -d postgres redis app nginx certbot
# Verify containers are running
docker compose ps
# Test application (from your VPN-connected machine)
curl http://10.13.13.1
# Or in browser: http://10.13.13.1
You should get responses from the VPN gateway.
### 7. Secure WireGuard Dashboard (After Setup)
After creating your initial clients, secure the WireGuard UI by editing `docker-compose.yaml`:
**Note:** SSL certificates are optional since the app is VPN-only. You can use self-signed certificates or skip SSL entirely for internal VPN access. However, if you want proper SSL:
### Option 1: Self-Signed Certificates (Recommended for VPN-only)
```bash
# Generate self-signed certificate
sudo mkdir -p docker/nginx/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout docker/nginx/ssl/selfsigned.key \
-out docker/nginx/ssl/selfsigned.crt \
-subj "/C=SI/ST=Slovenia/L=Ljubljana/O=TerenApp/CN=10.13.13.1"
# Update nginx config to use self-signed cert
vim docker/nginx/conf.d/app.conf
```
Update SSL paths:
```nginx
ssl_certificate /etc/nginx/ssl/selfsigned.crt;
ssl_certificate_key /etc/nginx/ssl/selfsigned.key;
```
```bash
# Restart nginx
docker compose restart nginx
# Test (ignore certificate warning)
curl -k https://10.13.13.1
```
### Option 2: Let's Encrypt (If using domain name)
Only if you have a domain pointing to your VPS and want valid SSL:
```bash
# Temporarily allow port 80 publicly
sudo ufw allow 80/tcp
# Request certificate
docker compose run --rm certbot certonly \
--standalone \
--preferred-challenges http \
--email your-email@example.com \
--agree-tos \
--no-eff-email \
-d your-domain.com
# Block port 80 again
sudo ufw delete allow 80/tcp
# Update nginx config with Let's Encrypt paths
# Restart nginx
docker compose restart nginx
```
### Option 3: No SSL (HTTP Only)
For internal VPN-only access, you can skip SSL entirely:
```bash
# Edit nginx config to remove HTTPS server block
vim docker/nginx/conf.d/app.conf
# Keep only the HTTP (port 80) server block
# Restart nginx
docker compose restart nginx
# Access via: http://10.13.13.1
```bash
# Create project directory
sudo mkdir -p /var/www
sudo chown -R $USER:$USER /var/www
cd /var/www
# Clone from your Gitea server
git clone git@your-gitea-server.com:username/Teren-app.git
cd Teren-app
```
### 2. Setup Environment
```bash
# Copy example files
cp .env.production.example .env
cp docker-compose.yaml.example docker-compose.yaml
# Edit .env file with your actual values
vim .env
```
**Important:** Update these values in `.env`:
- `APP_KEY` - Generate with `php artisan key:generate` (run after first container start)
- `APP_URL` - Your domain
- `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD` - Strong database credentials
- `PGADMIN_EMAIL`, `PGADMIN_PASSWORD` - pgAdmin credentials
### 3. Update Docker Compose
Edit `docker-compose.yaml` and update:
- Replace `example.com` with your domain in nginx volume mounts
- Update environment variables as needed
### 4. Create Required Directories
```bash
# Create docker directories
mkdir -p docker/nginx/conf.d
mkdir -p docker/nginx/ssl
mkdir -p docker/certbot/conf
mkdir -p docker/certbot/www
mkdir -p docker/postgres/init
mkdir -p docker/supervisor/conf.d
mkdir -p docker/php
# Set permissions
sudo chown -R $USER:$USER docker/
```
### 5. Update Nginx Configuration
Edit `docker/nginx/conf.d/app.conf` and replace `example.com` with your actual domain.
### 6. Initial SSL Certificate (HTTP-only first)
Before getting SSL certificates, modify nginx config temporarily:
```bash
# Comment out SSL sections in docker/nginx/conf.d/app.conf
# Keep only the HTTP (port 80) server block
# Start containers
docker compose up -d postgres redis app nginx
# Verify nginx is running
docker compose ps
curl http://your-domain.com # Should get some response
```
---
## SSL Certificate Setup
### 1. Obtain Initial Certificate
```bash
# Request certificate for your domain
docker compose run --rm certbot certonly \
--webroot \
--webroot-path=/var/www/certbot \
--email your-email@example.com \
--agree-tos \
--no-eff-email \
-d your-domain.com \
-d www.your-domain.com
```
### 2. Enable HTTPS in Nginx
```bash
# Uncomment SSL sections in docker/nginx/conf.d/app.conf
vimPortainer Setup
Portainer provides a web UI for managing Docker containers, images, networks, and volumes.
### 1. Access Portainer
**Prerequisites:** Must be connected to WireGuard VPN
**URL:** `http://10.13.13.1:9000` or `https://10.13.13.1:9443`
### 2. Initial Setup
On first access:
1. Create admin account:
- Username: `admin`
- Password: (set a strong password)
2. Select **"Docker"** environment
3. Click **"Connect"**
### 3. Portainer Features
- **Containers:** Start, stop, restart, view logs
- **Images:** Pull, build, remove images
- **Networks:** Manage Docker networks
- **Volumes:** View and manage volumes
- **Stacks:** Deploy docker-compose stacks
- **Events:** Real-time Docker events
### 4. Useful Portainer Operations
**View Container Logs:**
1. Go to Containers
2. Click on container name
3. Click "Logs"
4. Select "Auto-refresh"
**Execute Commands:**
1. Go to Containers
2. Click on container
3. Click "Console"
4. Select "/bin/sh" or "/bin/bash"
**Update Container:**
1. Go to Containers
2. Click container name
3. Click "Recreate"
4. Enable "Pull latest image"
### 5. Security Notes
- Portainer is only accessible via VPN (bound to 10.13.13.1)
- Always use strong passwords
- Enable 2FA if deploying Portainer Business Edition
- Regularly backup Portainer data volume
- "127.0.0.1:5432:5432"
# To:
ports:
- "10.13.13.1:5432:5432" # WireGuard VPN subnet
```
Then restart:
```bash
docker compose down
docker compose up -d
```
### 5. Connect Client
Install WireGuard on your local machine:
- **Linux:** `sudo apt install wireguard`
- **macOS:** Download from App Store
- **Windows:** Download from wireguard.com
Import the client config and connect.
### 6. Access Services via VPN
Once connected to VPN:
- pgAdmin: `http://10.13.13.1:5050`
- PostgreSQL: `10.13.13.1:5432`
---
## Automated Deployment Setup
### Method 1: Gitea Webhooks (Recommended)
#### 1. Setup Webhook Listener on VPS
```bash
# Create webhook handler script
sudo vim /usr/local/bin/webhook-handler.sh
```
Add:
```bash
#!/bin/bash
# Simple webhook handler
PORT=9000
SECRET="your-webhook-secret"
while true; do
echo "Listening for webhooks on port $PORT..."
echo -e "HTTP/1.1 200 OK\n\n" | nc -l -p $PORT -q 1 > /tmp/webhook.log
# Verify secret (basic)
if grep -q "$SECRET" /tmp/webhook.log; then
echo "Valid webhook received, deploying..."
cd /var/www/Teren-app && ./deploy.sh >> /var/log/deploy.log 2>&1
fi
done
```
```bash
# Make executable
sudo chmod +x /usr/local/bin/webhook-handler.sh
# Create systemd service
sudo vim /etc/systemd/system/webhook-handler.service
```
Add:
```ini
[Unit]
Description=Gitea Webhook Handler
After=network.target
[Service]
Type=simple
User=deployer
ExecStart=/usr/local/bin/webhook-handler.sh
Restart=always
[Install]
WantedBy=multi-user.target
```
```bash
# Enable and start
sudo systemctl enable webhook-handler
sudo systemctl start webhook-handler
# Allow webhook port in firewall
sudo ufw allow 9000/tcp
```
#### 2. Configure Gitea Webhook
In your Gitea repository:
1. Go to Settings → Webhooks
2. Add webhook:
- Payload URL: `http://your-vps-ip:9000`
- Secret: `your-webhook-secret`
- Trigger: Push events on `main` branch
3. Test webhook
### Method 2: Gitea Actions (Recommended for CI/CD)
#### 1. Enable Gitea Actions
On your Gitea server, edit `app.ini`:
```ini
[actions]
ENABLED = true
```
#### 2. Setup Actions Runner on VPS
```bash
# Download runner
cd ~
wget https://gitea.com/gitea/act_runner/releases/latest/download/act_runner-linux-amd64
chmod +x act_runner-linux-amd64
sudo mv act_runner-linux-amd64 /usr/local/bin/act-runner
# Register runner
act-runner register --instance https://your-gitea.com --token YOUR_RUNNER_TOKEN
# Generate config
act-runner generate-config > runner-config.yaml
# Create systemd service
sudo vim /etc/systemd/system/act-runner.service
```
Add:
```ini
[Unit]
Description=Gitea Actions Runner
After=network.target
[Service]
Type=simple
User=deployer
WorkingDirectory=/home/deployer
ExecStart=/usr/local/bin/act-runner daemon --config /home/deployer/runner-config.yaml
Restart=always
[Install]
WantedBy=multi-user.target
```
```bash
sudo systemctl enable act-runner
sudo systemctl start act-runner
```
#### 3. Create Workflow File
In your repository, create `.gitea/workflows/deploy.yaml`:
```yaml
name: Deploy to Production
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.VPS_HOST }}
username: deployer
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/Teren-app
./deploy.sh
```
Add secrets in Gitea: Settings → Secrets → Actions
- `VPS_HOST`: Your VPS IP/domain
- `SSH_PRIVATE_KEY`: SSH key for deployer user
### 3. Make Deploy Script Executable
```bash
cd /var/www/Teren-app
chmod +x deploy.sh
# Edit script to match your setup
vim deploy.sh
```
### 4. Test Deployment
```bash
# Manual test
./deploy.sh
# Or trigger via git push
git commit --allow-empty -m "Test deployment"
git push origin main
```
---
## Application Initialization
### 1. Start All Containers
```bash
cd /var/www/Teren-app
docker compose up -d
```
### 2. Generate Application Key
```bash
docker compose exec app php artisan key:generate
```
### 3. Run Migrations
```bash
docker compose exec app php artisan migrate --force
```
### 4. Seed Database (if needed)
```bash
docker compose exec app php artisan db:seed --force
```
### 5. Cache Configuration
```bash
docker compose exec app php artisan config:cache
docker compose exec app php artisan route:cache
docker compose exec app php artisan view:cache
```
### 6. Set Permissions
```bash
docker compose exec app chown -R www:www /var/www/storage
docker compose exec app chown -R www:www /var/www/bootstrap/cache
```
### 7. Verify Installation
```bash
# Check containers
docker compose ps
# Check logs
docker compose logs -f app
# Test application
curl https://your-domain.com
```
---
## Monitoring & Maintenance
### Container Management
```bash
# View all containers
docker compose ps
# View logs
docker compose logs -f app # Laravel app
docker compose logs -f nginx # Nginx
docker compose logs -f postgres # Database
# Restart containers
docker compose restart app
docker compose restart nginx
# Stop all
docker compose down
# Start all
docker compose up -d
```
### Queue Workers
```bash
# Check queue worker status
docker compose exec app supervisorctl status
# Restart qServices via VPN
**All services are only accessible when connected to WireGuard VPN:**
| Service | URL | Purpose |
|---------|-----|---------|
| Laravel App | `http://10.13.13.1` | Main application |
| pgAdmin | `http://10.13.13.1:5050` | PostgreSQL UI |
| Portainer | `http://10.13.13.1:9000` | Docker management |
| WireGuard UI | `http://10.13.13.1:51821` | VPN management |
| PostgreSQL | `10.13.13.1:5432` | Direct DB connection |
| Redis | `10.13.13.1:6379` | Direct Redis connection |
**pgAdmin Setup:**
1. Connect to VPN
2. Open: `http://10.13.13.1:5050`
3. Login with `PGADMIN_EMAIL` and `PGADMIN_PASSWORD`
4. Add server:
- Host: `postgres` (or `10.13.13.1` if connecting externally)nt
```bash
# Connect to PostgreSQL
docker compose exec postgres psql -U teren_user -d teren_app
# Backup database
docker compose exec postgres pg_dump -U teren_user teren_app > backup-$(date +%Y%m%d).sql
# Restore database
docker compose exec -T postgres psql -U teren_user teren_app < backup-20260113.sql
```
### Access pgAdmin
Via WireGuard VPN:
1. Connect to VPN
2. Open browser: `http://10.13.13.1:5050`
3. Login with `PGADMIN_EMAIL` and `PGADMIN_PASSWORD`
4. Add server:
- Host: `postgres`
- Port: `5432`
- Database: Your `DB_DATABASE`
- Username: Your `DB_USERNAME`
- Password: Your `DB_PASSWORD`
### Log Rotation
```bash
# Install logrotate
sudo apt install logrotate -y
# Create config
sudo vim /etc/logrotate.d/teren-app
```
Add:
```
/var/www/Teren-app/storage/logs/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 deployer deployer
sharedscripts
}
```
### Automated Backups
Create backup script:
```bash
sudo vim /usr/local/bin/backup-teren.sh
```
Add:
```bash
#!/bin/bash
BACKUP_DIR="/backups/teren-app"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# Backup database
docker compose -f /var/www/Teren-app/docker-compose.yaml exec -T postgres \
pg_dump -U teren_user teren_app | gzip > $BACKUP_DIR/db-$DATE.sql.gz
# Backup uploads
tar -czf $BACKUP_DIR/storage-$DATE.tar.gz -C /var/www/Teren-app storage/app/public
# Keep only last 7 days
find $BACKUP_DIR -type f -mtime +7 -delete
echo "Backup completed: $DATE"
```
```bash
sudo chmod +x /usr/local/bin/backup-teren.sh
# Add to crontab
crontab -e
```
Add:
```
0 2 * * * /usr/local/bin/backup-teren.sh >> /var/log/backup.log 2>&1
```
---
## Troubleshooting
### Container Won't Start
```bash
# Check logs
docker compose logs app
# Check container status
docker compose ps
# Rebuild container
docker compose down
docker compose build --no-cache app
docker compose up -d
```
### Permission Errors
```bash
# Fix storage permissions
docker compose exec app chown -R www:www /var/www/storage
docker compose exec app chmod -R 775 /var/www/storage
```
### Database Connection Issues
```bash
# Check if PostgreSQL is running
docker compose ps postgres
# Check PostgreSQL logs
docker compose logs postgres
# Test connection
docker compose exec app php artisan tinker
# In tinker: DB::connection()->getPdo();
```
### Queue Not Processing
```bash
# Check supervisor status
docker compose exec app supervisorctl status
# Restart queue workers
docker compose exec app supervisorctl restart all
# Check worker logs
docker compose exec app tail -f storage/logs/worker.log
```
# Check kernel module
lsmod | grep wireguard
# Check WireGuard interface
docker compose exec wireguard wg show
```
### Can't Access WireGuard Dashboard
```bash
# Check if container is running
docker compose ps wireguard
# Check logs
docker compose logs wireguard
# Verify port binding
sudo netstat -tulpn | grep 51821
# Try accessing via IP
curl http://YOUR_VPS_IP:51821
```
### Can't Access Application After VPN Connection
```bash
# Verify VPN connection
ping 10.13.13.1
# Check routing table (on client)
ip route | grep 10.13.13.0
# Check if services are bound correctly
docker compose exec app netstat -tulpn
# Check nginx logs
docker compose logs nginx
# Verify nginx is listening on VPN IP
docker compose exec nginx netstat -tulpn | grep 80
### SSL Certificate Issues
```bash
# Check certificate expiry
docker compose run --rm certbot certificates
# Renew manually
docker compose run --rm certbot renew
# Check nginx config
docker compose exec nginx nginx -t
# Reload nginx
docker compose restart nginx
```
### WireGuard Not Working
```bash
# Check WireGuard status
docker compose logs wireguard
# Restart WireGuard
docker compose restart wireguard
# Check firewall
sudo ufw status
# Verify port is open
sudo ss -tulpn | grep 51820
```
### High Memory Usage
```bash
# ChecWireGuard VPN configured and working
- [ ] WireGuard UI secured (VPN-only after initial setup)
- [ ] All services bound to VPN network (10.13.13.1)
- [ ] Firewall configured (UFW) - only VPN port public
- [ ] SSH key authentication enabled
- [ ] Strong passwords for all services (DB, pgAdmin, Portainer, WireGuard)
- [ ] `.env` file not in git
- [ ] SSL certificates configured (self-signed or Let's Encrypt)
- [ ] Regular backups automated
- [ ] Log rotation configured
- [ ] Security headers in nginx
- [ ] Docker containers run as non-root
- [ ] Regular system updates scheduled
- [ ] WireGuard client configs secur
### Application Returns 500 Error
```bash
# Check Laravel logs
docker compose exec app tail -f storage/logs/laravel.log
# Check permissions
docker compose exec app ls -la storage/
# Clear caches
docker compose exec app php artisan cache:clear
docker compose exec app php artisan config:clear
docker compose exec app php artisan route:clear
docker compose exec app php artisan view:clear
```
---
## Security Checklist
- [ ] Firewall configured (UFW)
- [ ] SSH key authentication enabled
- [ ] Strong database passwords
- [ ] `.env` file not in git
- [ ] Services behind WireGuard VPN
- [ ] SSL certificates valid
- [ ] Regular backups automated
- [ ] Log rotation configured
- [ ] Security headers in nginx
- [ ] Docker containers run as non-root
- [ ] Regular system updates scheduled
---
## Useful Commands Reference
```bash
# Deploy new version
./deploy.sh
# View all logs
docker compose logs -f
# Restart everything
docker compose restart
# Update single container
docker compose up -d --no-deps --build app
# Run artisan commands
docker compose exec app php artisan [command]
# Access container shell
docker compose exec app sh
# Database backup
docker compose exec postgres pg_dump -U teren_user teren_app > backup.sql
# System monitoring
htop
docker stats
df -h
```
---
## Support & Updates
For issues specific to the Teren App, check the application logs and supervisor status. For Docker/infrastructure issues, check container logs and system logs.
To update the deployment guide or Docker configuration, submit changes via Gitea and they'll be automatically deployed.
---
**Last Updated:** January 2026
**Version:** 1.0.0