Skip to content

Server Project Organization Guide

Server: production-server-01 (Hetzner) Last Updated: 2026-04-11


This guide explains the directory organization patterns used on our production server, helping you understand where projects are located and how they’re deployed.


Our server uses two main directories for hosting applications, each serving different deployment types:

Purpose: Direct deployment applications running on the host system

Characteristics:

  • Applications run as systemd services or PM2 processes
  • Dependencies installed directly on the host
  • Share host system resources (PostgreSQL, Redis, Node.js, Python)
  • Managed via systemd or process managers

Examples:

  • KopanoWorks - Construction project management (pnpm monorepo)
  • ProspectIQ - Lead enrichment platform (FastAPI + Celery)
  • ThriveSend - Email marketing platform (Node.js)
  • SANSA - Space education platform (Frontend + Backend)

Purpose: Self-contained Docker-based applications

Characteristics:

  • Applications run in Docker containers via Docker Compose
  • All dependencies bundled inside containers
  • Isolated environments with their own databases, web servers, etc.
  • Managed via docker compose commands

Examples:

  • Polelo DMS - Document management system (Laravel + Docker)
  • AutoSlip - Automated slip system
  • Buka - Containerized application

This organization follows Linux best practices:

DirectoryPurposeStandard Usage
/var/www/Variable data for web servicesWeb content, applications served by web servers
/opt/Optional/add-on software packagesSelf-contained third-party or containerized applications

Benefits:

  • ✅ Clear separation between deployment types
  • ✅ Easy to understand how each project is managed
  • ✅ Prevents confusion during maintenance and troubleshooting
  • ✅ Follows industry-standard conventions

ProjectLocationTypePort(s)Domain
KopanoWorks/var/www/kopanoworks/pnpm monorepo (Express + Next.js + React Native)Variouskopanoworks.isutech.co.za, admin.kopanoworks.isutech.co.za
ProspectIQ/var/www/prospect-iq/FastAPI + Celery8002, 3002prospectiq.isutech.co.za
ThriveSend/var/www/thrivesend/Node.jsVariousthrivesend.isutech.co.za
SANSA Frontend/var/www/sansa-frontend/Next.js3003sansa.isutech.co.za
SANSA Backend/var/www/sansa-backend/Node.js + Express8003sansa.isutech.co.za/api
AssessFlow/var/www/assess-flow/Web applicationVariousassess-flow.isutech.co.za
ConformEdge/var/www/conformedge/Web applicationVariousconformedge.isutech.co.za
DealVault/var/www/dealvault/Web applicationVariousdealvault.isutech.co.za
PropertyData Engine/var/www/propertydata-engine/Data processing engineVariouspropertydata-engine.isutech.co.za
Tumi Frontend/var/www/tumi-frontend/Frontend applicationVarioustumi.isutech.co.za
Tumi Backend/var/www/tumi-backend/Backend APIVarioustumi.isutech.co.za/api
BuildQ/var/www/buildq/Construction platformVariousbuildq.isutech.co.za
Umdoni/var/www/umdoni/Municipal websiteVariousumdoni.gov.za
DocsHub/var/www/docs/MkDocs documentation site5001 (API)docs.isutech.co.za
ProjectLocationTypeDocker PortsDomain
Polelo DMS/opt/polelo-dms/Laravel + Nextcloud + PostgreSQL + Redis + Meilisearch8100 (nginx), 8180 (nextcloud)polelodms.isutech.co.za
AutoSlip/opt/AutoSlip/Containerized applicationVariousautoslip.isutech.co.za
Buka/opt/buka/Containerized applicationVariousbuka.isutech.co.za

Polelo DMS is a perfect example of why containerized apps live in /opt/:

┌─────────────────────────────────────────┐
│ Polelo DMS (Laravel) │ Custom business logic
│ Workflows, File Plans, Audit, Reports │
├─────────────────────────────────────────┤
│ Nextcloud │ Document storage, sync, mobile
│ Storage, Sync, Versioning, Sharing │
├─────────────────────────────────────────┤
│ PostgreSQL + Redis + Meilisearch │ Data layer
└─────────────────────────────────────────┘
Terminal window
polelo-nginx # Nginx web server (port 8100)
polelo-app # Laravel PHP application
polelo-nextcloud # Nextcloud platform (port 8180)
polelo-meilisearch # Search engine
polelo-redis # Cache/queue
  1. Docker containers run at /opt/polelo-dms/
  2. Nginx container exposes port 8100 internally
  3. Host Nginx proxies polelodms.isutech.co.zahttp://127.0.0.1:8100
  4. SSL certificate managed by Let’s Encrypt on the host

Terminal window
# List all projects in /var/www/
ls -la /var/www/
# List all containerized apps in /opt/
ls -la /opt/
# Find a specific project by name
find /var/www /opt -maxdepth 2 -type d -iname "*project-name*" 2>/dev/null
Terminal window
# List all systemd services
systemctl list-units --type=service
# Check services for a specific project
systemctl list-units --type=service | grep -i project-name
# Check status of a specific service
systemctl status service-name
Terminal window
# List all running Docker containers
docker ps
# List all containers (including stopped)
docker ps -a
# Check containers for a specific project
docker ps | grep project-name
# View Docker Compose services for a project
cd /opt/project-name
docker compose ps
# View logs for a container
docker logs container-name
# Follow logs in real-time
docker logs -f container-name
Terminal window
# List all enabled sites
ls /etc/nginx/sites-enabled/
# Search for a domain in nginx configs
grep -r "domain.com" /etc/nginx/sites-enabled/
# Test nginx configuration
nginx -t
# Reload nginx (after config changes)
systemctl reload nginx
Terminal window
# Check what's listening on all ports
ss -tulpn
# Check a specific port
ss -tulpn | grep :8100
# Find process using a port
lsof -i :8100
Terminal window
# Check project structure
ls -la /var/www/project-name/
ls -la /opt/project-name/
# Look for configuration files
ls /var/www/project-name/{.env,config/,*.yml,*.json} 2>/dev/null
# Check if project uses Docker
ls /opt/project-name/docker-compose*.yml 2>/dev/null
# View package.json for Node.js projects
cat /var/www/project-name/package.json
# View requirements.txt for Python projects
cat /var/www/project-name/requirements.txt
# View composer.json for PHP projects
cat /var/www/project-name/composer.json
Terminal window
# Check PM2 processes (Node.js apps)
pm2 list
pm2 info app-name
pm2 logs app-name
# Check systemd service logs
journalctl -u service-name -n 100 --no-pager
# Follow service logs
journalctl -u service-name -f
Terminal window
# Connect to PostgreSQL
psql -U username -d database_name
# List all PostgreSQL databases
psql -U postgres -c "\l"
# Connect to Redis
redis-cli
# Check Redis info
redis-cli info

Task/var/www/ Project/opt/ Project
View logsjournalctl -u service-name -f or pm2 logs app-namedocker logs -f container-name
Restartsystemctl restart service-name or pm2 restart app-namedocker compose restart
Check statussystemctl status service-name or pm2 statusdocker compose ps
View config.env, config/ folderdocker-compose.yml, .env
Update codegit pull, restart servicegit pull, docker compose up -d --build

  1. Decide on deployment type first:

    • Self-contained with multiple services? → Use Docker in /opt/
    • Traditional web app using host services? → Use /var/www/
  2. Follow naming conventions:

    • Use lowercase with hyphens: project-name
    • Be consistent with domain names
  3. Document your deployment:

    • Add to this inventory
    • Update nginx configs
    • Configure monitoring
  1. Always check the location first:

    Terminal window
    find /var/www /opt -name "project-name" -type d
  2. Understand the deployment type:

    • Check for docker-compose.yml → Containerized
    • Check for systemd service → Direct deployment
  3. Use appropriate commands:

    • Don’t try to use systemctl on Docker containers
    • Don’t try to use docker commands on systemd services

Terminal window
# Search everywhere
find /var/www /opt /home -name "*project*" -type d 2>/dev/null
Terminal window
# Check systemd
systemctl list-units | grep -i project
# Check Docker
docker ps | grep -i project
# Check PM2
pm2 list | grep -i project
# Check ports
ss -tulpn | grep -i project
Terminal window
# Search nginx configs
grep -r "project-name\|domain.com" /etc/nginx/sites-enabled/


Questions or Updates? If you discover new patterns or projects, please update this document to keep it current for the team.