💡
Context

This guide documents the full setup for a centralized development server — hosting 7 Laravel applications accessible to a distributed team via local network, Tailscale VPN, and Cloudflare Tunnel.

1. Overview & Architecture

The goal: one physical server that any developer can code on, no matter where they are — with zero data loss, clean URLs, and full HTTPS in production-like conditions.

  • Centralized environment — work from anywhere, nothing local to lose
  • Remote SSH access via Tailscale (encrypted WireGuard VPN)
  • Public web access via Cloudflare Tunnel (no port forwarding)
  • Multi-tenancy support with wildcard subdomains
  • HTTP Basic Auth gating all public-facing dev URLs
  • 7 Laravel applications on separate ports

2. Hardware Requirements

ComponentSpecificationNotes
ServerDell PowerEdge T340Repurposed workstation
CPUIntel Xeon E-2224 (4 cores)Upgradable to E-2234/2244
RAM16GB DDR3Recommend upgrading to 32GB
Storage480GB SSD + 2x 500GB HDDSSD for VMs, HDD for backups
NetworkGigabit EthernetIntel I210 adapter
OSWindows 11 ProRequired for Hyper-V

3. Hyper-V Setup

⚠️
Windows edition required

Hyper-V requires Windows 11 Pro, Enterprise, or Education. Home does not support it.

3.1 Enable Hyper-V

# Run PowerShell as Administrator
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
Restart-Computer

3.2 Create External Virtual Switch

# Find your adapter name
Get-NetAdapter

# Create external switch
New-VMSwitch -Name "External Switch" -NetAdapterName "Ethernet" -AllowManagementOS $true

3.3 Create the Dev-Server VM

1

New Virtual Machine

Hyper-V Manager → New → Virtual Machine → Name: Dev-Server

2

Generation & Resources

Generation 2 (UEFI). RAM: 10240 MB with Dynamic Memory (min 4096 MB). CPU: 3 virtual processors.

3

Networking & Storage

Connection: External Switch. Virtual hard disk: 200GB. Installation from ISO file.

4

Disable Secure Boot

Settings → Security → uncheck Enable Secure Boot (required for Ubuntu).

4. Ubuntu Server Installation

1

Set a Static IP

⚠️
Use a static IP

DHCP addresses can change and break every configuration that references the IP.

Subnet:      192.0.2.0/24
Address:     192.0.2.10
Gateway:     192.0.2.1
Name servers: 8.8.8.8,8.8.4.4
2

Post-Installation

sudo apt update && sudo apt upgrade -y
sudo apt install linux-tools-virtual linux-cloud-tools-virtual -y
sudo reboot

5. LAMP Stack Installation

5.1 Nginx

sudo apt install nginx -y
sudo systemctl enable nginx

5.2 PHP 8.3

sudo apt install php8.3-fpm php8.3-mysql php8.3-curl php8.3-xml \
  php8.3-mbstring php8.3-zip php8.3-gd php8.3-bcmath php8.3-intl \
  php8.3-redis php8.3-sqlite3 -y

5.3 MySQL & Composer

sudo apt install mysql-server -y
sudo systemctl enable mysql

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

5.4 Additional Tools & Directory Structure

sudo apt install git nodejs npm redis-server supervisor curl unzip wget htop -y

sudo mkdir -p /var/www/{app1,app2,app3,app4,app5,app6,app7}
sudo chown -R www-data:www-data /var/www
sudo chmod -R 755 /var/www

6. Laravel Application Deployment

cd /var/www/myapp
sudo -u www-data git clone https://github.com/your-org/your-repo.git ./
sudo -u www-data composer install --no-dev --optimize-autoloader
sudo -u www-data npm install && sudo -u www-data npm run build
sudo -u www-data cp .env.example .env
sudo -u www-data php artisan key:generate
sudo -u www-data php artisan migrate --seed
⚠️
Keep APP_DEBUG=false for any public-facing URL

Debug mode exposes DB credentials, API keys, file paths, and stack traces.

7. Nginx Configuration

server {
    listen 8001;
    listen [::]:8001;
    server_name 192.0.2.10.nip.io *.192.0.2.10.nip.io;
    root /var/www/myapp/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    index index.php;
    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* { deny all; }
}
PortApp
8001ADN-ERP
8002CRM
8003HRMIS
8004Rental
8005Hospital
8006E-commerce
8007Booking

8. Multi-Tenancy & nip.io

💡
How nip.io works

192.0.2.10.nip.io resolves to 192.0.2.10. Any subdomain like tenant1.192.0.2.10.nip.io also resolves to the same IP. No DNS setup needed.

# Laravel .env for nip.io
APP_URL=http://192.0.2.10.nip.io:8001
ROOT_DOMAIN=192.0.2.10.nip.io
SESSION_DOMAIN=.192.0.2.10.nip.io

9. Tailscale — Remote SSH Access

Tailscale creates a zero-config encrypted VPN using WireGuard. SSH into the dev server from anywhere without exposing port 22.

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

# From anywhere:
ssh devuser@dev-server

10. Cloudflare Tunnel — Public Web Access

wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb
cloudflared tunnel login
cloudflared tunnel create dev-tunnel

Tunnel Config

# /etc/cloudflared/config.yml
tunnel: YOUR-TUNNEL-ID
credentials-file: /home/devuser/.cloudflared/YOUR-TUNNEL-ID.json

ingress:
  - hostname: myapp.example.com
    service: http://localhost:8001
  - hostname: "*.myapp.example.com"
    service: http://localhost:8001
  - hostname: app2.example.com
    service: http://localhost:8002
  - service: http_status:404
sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared

11. Security — HTTP Basic Auth

⚠️
The risk of public dev servers

Even with APP_DEBUG=false, a publicly accessible dev server exposes error pages and gives attackers a larger attack surface. Gate it.

sudo apt install apache2-utils -y
sudo htpasswd -c /etc/nginx/.htpasswd devuser

Smart Nginx config — auth only on external (Cloudflare) requests:

# Detect Cloudflare traffic by X-Forwarded-For header
map $http_x_forwarded_for $auth_basic_realm {
    default "Dev Server - Login Required";
    ""      off;
}

server {
    auth_basic $auth_basic_realm;
    auth_basic_user_file /etc/nginx/.htpasswd;
    ...
}
  • Cloudflare URLs prompt for username/password
  • Local network access — no prompt
  • Tailscale access — no prompt

12. Testing & Verification

# Service status check
for svc in nginx php8.3-fpm mysql redis-server tailscaled cloudflared; do
  systemctl is-active $svc && echo "OK $svc" || echo "FAIL $svc"
done

# Ports listening
sudo ss -tlnp | grep -E ':(8001|8002|8003|8004|8005|8006|8007)\s'

13. Troubleshooting

502 Bad Gateway

sudo systemctl status php8.3-fpm
sudo systemctl start php8.3-fpm
sudo tail -20 /var/log/nginx/error.log

500 Internal Server Error

sudo tail -50 /var/www/myapp/storage/logs/laravel.log

Permission Denied

sudo chown -R www-data:www-data /var/www/myapp
sudo chmod -R 775 /var/www/myapp/storage /var/www/myapp/bootstrap/cache

14. Maintenance & Backups

Database Backup Script

#!/bin/bash
BACKUP_DIR="/var/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR

for DB in app1_dev app2_dev app3_dev app4_dev app5_dev app6_dev app7_dev; do
    mysqldump -u root $DB | gzip > $BACKUP_DIR/${DB}_${DATE}.sql.gz
done

find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete

Maintenance Checklist

  • Daily: check services, review error logs, monitor disk space
  • Weekly: system updates, clear old logs, security review
  • Monthly: update Composer deps, rotate credentials, full backup
Setup complete

If all 14 sections are done, you have a fully working centralized dev server — accessible locally, remotely via SSH, and publicly via Cloudflare — with multi-tenancy, HTTPS, and HTTP Basic Auth protecting public URLs.