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
| Component | Specification | Notes |
|---|---|---|
| Server | Dell PowerEdge T340 | Repurposed workstation |
| CPU | Intel Xeon E-2224 (4 cores) | Upgradable to E-2234/2244 |
| RAM | 16GB DDR3 | Recommend upgrading to 32GB |
| Storage | 480GB SSD + 2x 500GB HDD | SSD for VMs, HDD for backups |
| Network | Gigabit Ethernet | Intel I210 adapter |
| OS | Windows 11 Pro | Required for Hyper-V |
3. Hyper-V Setup
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
New Virtual Machine
Hyper-V Manager → New → Virtual Machine → Name: Dev-Server
Generation & Resources
Generation 2 (UEFI). RAM: 10240 MB with Dynamic Memory (min 4096 MB). CPU: 3 virtual processors.
Networking & Storage
Connection: External Switch. Virtual hard disk: 200GB. Installation from ISO file.
Disable Secure Boot
Settings → Security → uncheck Enable Secure Boot (required for Ubuntu).
4. Ubuntu Server Installation
Set 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
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
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; }
}
| Port | App |
|---|---|
| 8001 | ADN-ERP |
| 8002 | CRM |
| 8003 | HRMIS |
| 8004 | Rental |
| 8005 | Hospital |
| 8006 | E-commerce |
| 8007 | Booking |
8. Multi-Tenancy & nip.io
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
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
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.