# Production Deployment — Ubuntu 24.04

> Ubuntu 26.04 does not exist at time of writing. These steps target **Ubuntu 24.04 LTS** (Noble), which is the current LTS. Adapt package names when 26.04 is released.

---

## 1. System packages

```bash
sudo apt update && sudo apt upgrade -y
sudo apt install -y git unzip curl apache2 sqlite3 supervisor
```

---

## 2. PHP 8.5

Install PHP 8.5 via the Ondřej PPA:

```bash
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
sudo apt install -y php8.5 php8.5-cli \
    php8.5-mbstring php8.5-xml php8.5-curl \
    php8.5-sqlite3 php8.5-zip php8.5-bcmath \
    libapache2-mod-php8.5
```

Verify:

```bash
php -v
```

---

## 3. Composer

```bash
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
```

---

## 4. Deploy the application

```bash
sudo mkdir -p /var/www/vision-monday-connector
sudo chown ubuntu:ubuntu /var/www/vision-monday-connector

git clone <repo-url> /var/www/vision-monday-connector
cd /var/www/vision-monday-connector

# App files owned by ubuntu (for git and artisan commands)
sudo chown -R ubuntu:ubuntu /var/www/vision-monday-connector

# Storage and cache must be writable by www-data (Apache) and ubuntu (artisan)
sudo chown -R ubuntu:www-data /var/www/vision-monday-connector/storage
sudo chown -R ubuntu:www-data /var/www/vision-monday-connector/bootstrap/cache
sudo chmod -R 775 /var/www/vision-monday-connector/storage
sudo chmod -R 775 /var/www/vision-monday-connector/bootstrap/cache

# Add ubuntu to www-data group so it can write to storage
sudo usermod -aG www-data ubuntu
newgrp www-data

composer install --no-dev --optimize-autoloader
cp .env.example .env
php artisan key:generate
```

---

## 5. Environment variables

Edit `/var/www/vision-monday-connector/.env` and set at minimum:

```ini
APP_ENV=production
APP_DEBUG=false
APP_URL=https://your-domain.com

LOG_CHANNEL=daily
LOG_LEVEL=warning

QUEUE_CONNECTION=database

# Velon
VELON_BASE_URL=https://your-velon-instance.com
VELON_API_TOKEN=<token Velon uses to authenticate to this connector>
VELON_API_TIMEOUT=25

# Monday.com
MONDAY_API_TOKEN=<monday oauth token>
MONDAY_API_TIMEOUT=60
```

Then cache config:

```bash
php artisan config:cache
php artisan route:cache
```

---

## 6. SQLite queue database

The queue uses SQLite. Create the file and run migrations:

```bash
touch /var/www/vision-monday-connector/database.sqlite
php artisan migrate --force
```

---

## 7. Apache

Enable required modules:

```bash
sudo a2enmod rewrite
sudo a2enmod php8.5
```

Create `/etc/apache2/sites-available/vision-monday-connector.conf`:

```apache
<VirtualHost *:80>
    ServerName your-domain.com
    DocumentRoot /var/www/vision-monday-connector/public

    <Directory /var/www/vision-monday-connector/public>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/connector-error.log
    CustomLog ${APACHE_LOG_DIR}/connector-access.log combined
</VirtualHost>
```

Enable the site and reload:

```bash
sudo a2ensite vision-monday-connector
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2
```

Laravel's `public/.htaccess` handles URL rewriting — no extra config needed as long as `AllowOverride All` is set.

For HTTPS, install Certbot:

```bash
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache -d your-domain.com
```

---

## 8. PHP configuration fixes

### PCRE JIT (EC2 security restriction)

EC2 instances block the executable memory allocation that PCRE JIT requires, causing a PHP warning that crashes Laravel's bootstrap. Disable it:

```bash
echo "pcre.jit=0" | sudo tee /etc/php/8.5/apache2/conf.d/99-pcre-jit.ini
sudo systemctl restart apache2
```

### OPcache

Without OPcache, PHP recompiles every file on every request. Enable it:

```bash
sudo nano /etc/php/8.5/apache2/conf.d/10-opcache.ini
```

Add:

```ini
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
```

```bash
sudo systemctl restart apache2
```

### File upload limits

By default PHP allows only 2MB uploads. Raise the limits in the Apache PHP ini:

```bash
sudo nano /etc/php/8.5/apache2/php.ini
```

Find and update:

```ini
upload_max_filesize = 50M
post_max_size = 55M
```

`post_max_size` must always be larger than `upload_max_filesize`. Then restart Apache:

```bash
sudo systemctl restart apache2
```

### Clear stale config cache

If `config:cache` was run before the `.env` was fully populated, it produces a broken cache (`Target class [view] does not exist`). Clear it and re-cache once the app is ready:

```bash
cd /var/www/vision-monday-connector
php artisan config:clear
php artisan cache:clear
php artisan config:cache
php artisan route:cache
```

---

## 9. Queue worker via Supervisor

Create `/etc/supervisor/conf.d/vision-monday-connector.conf`:

```ini
[program:connector-queue-worker]
command=php /var/www/vision-monday-connector/artisan queue:work database --sleep=3 --tries=3 --max-time=3600
directory=/var/www/vision-monday-connector
autostart=true
autorestart=true
user=ubuntu
numprocs=1
stdout_logfile=/var/log/supervisor/connector-queue-worker.log
stderr_logfile=/var/log/supervisor/connector-queue-worker.log
```

Load it:

```bash
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start connector-queue-worker
sudo supervisorctl status
```

---

## 10. Laravel scheduler (cron)

The scheduler dispatches the daily reconciliation job. Add one cron entry as `ubuntu`:

```bash
crontab -e
```

Add:

```
* * * * * cd /var/www/vision-monday-connector && php artisan schedule:run >> /dev/null 2>&1
```

---

## 11. Verify

```bash
# App responds
curl https://your-domain.com/api/status

# Queue worker is running
sudo supervisorctl status connector-queue-worker

# Recent worker output (jobs processed / failed)
sudo tail -50 /var/log/supervisor/connector-queue-worker.log

# Scheduler fired recently
grep "schedule" /var/www/vision-monday-connector/storage/logs/laravel.log | tail -20

# Jobs sitting unprocessed in the queue
cd /var/www/vision-monday-connector && php artisan queue:monitor database

# Scheduler is registered
php artisan schedule:list
```

---

## Deploying updates

```bash
cd /var/www/vision-monday-connector
git pull
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
sudo supervisorctl restart connector-queue-worker
sudo systemctl reload apache2
```

---

## Optional: Switch queue storage to MySQL

By default the queue uses SQLite. If you expect traffic to grow, switch to MySQL for better write concurrency.

**Install MySQL:**

```bash
sudo apt install -y mysql-server
sudo mysql_secure_installation
```

**Create a database and user:**

```bash
sudo mysql -u root -p
```

```sql
CREATE DATABASE vision_monday_connector;
CREATE USER 'connector'@'localhost' IDENTIFIED BY 'your-password';
GRANT ALL PRIVILEGES ON vision_monday_connector.* TO 'connector'@'localhost';
FLUSH PRIVILEGES;
EXIT;
```

**Update `.env`:**

```ini
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=vision_monday_connector
DB_USERNAME=connector
DB_PASSWORD=your-password
```

`QUEUE_CONNECTION=database` stays the same — Laravel's database queue driver automatically uses the configured DB connection.

**Run migrations and restart the worker:**

```bash
php artisan migrate --force
sudo supervisorctl restart connector-queue-worker
```

You can delete `database.sqlite` once the worker is confirmed running cleanly against MySQL.
