# 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
