VPS Deployment Documentation
Backend & Frontend setup guides — with Docker, and manually without Docker.
1. Overview
This document covers two complete ways to deploy PathForge (backend + frontend) on a VPS: using Docker Compose, or manually without Docker, served through Nginx.
Each setup video below is paired with the exact written steps used in it. Replace placeholders such as <VPS_IP>, <VPS_USER>, <BACKEND_REPO_URL> and <FRONTEND_REPO_URL> with your actual values throughout.
REQUIRES
A VPS with SSH access (Ubuntu 22.04 / 24.04 recommended) is required for both deployment paths.
2. Deployment with Docker
This path covers deploying both the backend and the frontend on a VPS using Docker Compose.
| Service | URL | Port |
|---|---|---|
| Backend | http://<VPS_IP>:8585 | 8585 |
| Frontend | http://<VPS_IP>:8485 | 8485 |
2.1 Prerequisites
- A VPS with SSH access (Ubuntu 22.04 / 24.04 recommended)
- SSH credentials (password or private key)
- Git installed on the VPS
- Docker Engine and the Docker Compose plugin installed on the VPS
- Ports 8585 and 8485 open in the firewall / cloud provider security group
Verify (or install) the prerequisites
git --version
docker --version
docker compose version
Install Docker if missing:
sudo apt update && sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg git
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER
newgrp docker
2.2 Setting up Backend in VPS via Docker
Part 1 of the Docker deployment guide
Step 1 — SSH into the VPS
ssh <VPS_USER>@<VPS_IP>
Step 2 — Navigate to deployment root
cd /var/www
Step 3 — Clone the backend repository
git clone https://github.com/Ahmad-Mujtaba-Saeed/backendAndTechTrack.git
Step 4 — Enter project directory & configure .env
cd backendAndTechTrack
cp .env.example .env
nano .env
Step 5 — Build and run Docker containers
docker compose up --build -d
Step 6 — Verify backend service
Open in browser: http://<VPS_IP>}:8585
2.3 Setting up Frontend on VPS via Docker
Part 2 of the Docker deployment guide
Step 1 — Navigate to deployment root
cd /var/www
Step 2 — Clone frontend repository
git clone https://github.com/Ahmad-Mujtaba-Saeed/aTechTrackFrontend.git
Step 3 — Build and start frontend container
cd aTechTrackFrontend
docker compose up --build -d
Step 4 — Verify Frontend application
Open in browser: http://<VPS_IP>:8485
2.4 Post-Deployment Checklist (Docker)
- Backend containers Up (`docker compose ps`)
- Frontend containers Up (`docker compose ps`)
- `http://
:8585` loads backend - `http://
:8485` loads frontend interface
2.5 Common Management Commands
Run these from inside the respective project directory.
# View running containers
docker compose ps
# View logs (all services)
docker compose logs -f
# View logs for one service
docker compose logs -f <service-name>
# Stop containers (keeps volumes/data)
docker compose down
# Stop containers AND delete volumes (destroys database data)
docker compose down -v
# Restart containers
docker compose restart
# Open a shell inside a container
docker compose exec <service-name> bash
2.6 Deploying Updates
cd /var/www/<project-folder>
git pull origin main
docker compose up --build -d
2.7 Troubleshooting
Port already in use
sudo lsof -i :8585
sudo lsof -i :8485
Stop whatever is holding the port, or change the host-side port mapping in docker-compose.yml.
Backend can't connect to the database
- Confirm DB_HOST in .env matches the database service name in docker-compose.yml (not localhost)
- Confirm the database container is running:
docker compose ps - Check the database logs:
docker compose logs <db-service-name> - Give the database a moment on first boot — it may need to initialize before the backend can connect
Site not reachable from the browser but curl localhost works on the VPS
This is a firewall issue, not a Docker issue:
sudo ufw status
Also check your cloud provider's security group / firewall rules for ports 8585 and 8485.
Build fails or behaves unexpectedly
Rebuild from scratch without the cache:
docker compose build --no-cache
docker compose up -d
Disk space filling up
docker system df
docker system prune -a
3. Manual Deployment (Without Docker)
Deploying Laravel (Backend) and Node.js (Frontend) directly on a VPS, served via Nginx.
3.1 Prerequisites
sudo apt update && sudo apt upgrade -y
sudo apt install -y git curl unzip nginx
3.2 Setting up Backend on VPS without Docker
Part 1 of the manual deployment guide — Laravel
Step 1 — Install PHP, Composer & Dependencies
sudo apt install -y php php-fpm php-cli php-mysql php-mbstring php-xml php-curl php-zip
composer install --no-dev --optimize-autoloader
Step 2 — Generate Key & Run Migrations
php artisan key:generate
php artisan migrate --force
php artisan db:seed --force
Step 3 — Set Storage Permissions
sudo chown -R www-data:www-data storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cache
Step 4 — Configure Nginx Site
sudo nano /etc/nginx/sites-available/backend
sudo ln -s /etc/nginx/sites-available/backend /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
3.3 Setting up Frontend on VPS without Docker
Part 2 of the manual deployment guide — Node.js
Step 1 — Configure API Endpoint
nano src/config/config.js # Point VITE_API_URL to backend URL
Step 2 — Build Static Files
npm install
npm run build
Step 3 — Nginx Config & Reload
sudo nano /etc/nginx/sites-available/frontend
sudo ln -s /etc/nginx/sites-available/frontend /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
3.4 Optional — Domain and HTTPS
If you're using domains rather than raw ports, point the DNS A records at your VPS IP, change both configs to listen 80; with the appropriate server_name, then issue certificates:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d api.yourdomain.com -d app.yourdomain.com
Certbot edits the Nginx configs in place and sets up automatic renewal. Verify renewal works:
sudo certbot renew --dry-run
REMEMBER
Update APP_URL in the backend .env and the API URL in the frontend .env to the https:// addresses, then re-run npm run build for the frontend.
3.5 Post-Deployment Checklist (Manual)
- PHP and Node v22 installed and configured correctly
- Nginx configuration syntax test (`sudo nginx -t`) passes
- Storage write permissions granted to `www-data`
- Frontend build outputs successfully served on port 8485
3.6 Deploying Updates
Backend:
cd /var/www/<backend-project-folder>
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
sudo chown -R www-data:www-data storage bootstrap/cache
sudo systemctl reload php8.2-fpm
Frontend:
cd /var/www/<frontend-project-folder>
git pull origin main
npm install
npm run build
No Nginx reload is needed for a frontend rebuild — it serves the files from disk.
3.7 Useful Commands
# Nginx
sudo nginx -t # test the config
sudo systemctl reload nginx # apply config changes
sudo systemctl status nginx
sudo tail -f /var/log/nginx/backend-error.log
# PHP-FPM
sudo systemctl status php8.2-fpm
sudo systemctl restart php8.2-fpm
# Laravel
php artisan config:clear # after editing .env
php artisan cache:clear
php artisan route:clear
tail -f storage/logs/laravel.log
# MySQL
sudo systemctl status mysql
mysql -u app_user -p app_db
3.8 Troubleshooting
500 error or a blank white page on the backend
Check the Laravel log first — it names the actual cause:
tail -50 storage/logs/laravel.log
tail -50 /var/log/nginx/backend-error.log
The usual culprits, in order of likelihood: storage / bootstrap/cache permissions, a missing APP_KEY, or wrong database credentials.
"No application encryption key has been specified"
php artisan key:generate
php artisan config:clear
Config changes in .env aren't taking effect
Laravel caches the config. After any .env edit:
php artisan config:clear
php artisan config:cache
502 Bad Gateway
Nginx can't reach PHP-FPM. Confirm the socket path in your config matches reality:
ls /var/run/php/
sudo systemctl status php8.2-fpm
Database connection refused
sudo systemctl status mysql
mysql -u app_user -p -h 127.0.0.1 app_db
Confirm DB_HOST=127.0.0.1 in .env — not a Docker service name left over from another setup.
Python packages import but the feature still fails
Almost always the missing system binaries rather than the Python packages:
pdftoppm -v # poppler-utils, needed by pdf2image
tesseract --version # needed by pytesseract
Also check that the www-data user can run them — PHP shells out as www-data, not as your login user.
Frontend loads but shows a blank page
Check the browser console. If assets 404, the Nginx root is pointing at the wrong build directory — confirm whether the project builds to dist/ or build/.
Frontend routes 404 on refresh
The try_files $uri $uri/ /index.html; line is missing from the Nginx config.
Port not reachable from outside though curl localhost works
Firewall, not Nginx:
sudo ufw status
Also check your cloud provider's security group rules for ports 8585 and 8485.