Forget GitHub Actions. Forget Cloudflare Pages. This is how we ACTUALLY deploy: 1. npm run build 2. scp dist.tar.gz pi@192.168.4.64:/tmp/ 3. docker run -d -p PORT:80 nginx:alpine 4. Done in 30 seconds Architecture: Internet → Cloudflare → Tunnel → Pi → Caddy → Docker Real example: Just deployed lucidia.earth 3D metaverse - Three.js + Cannon.js physics - Procedural cityscape + floating islands - Live at https://lucidia.earth - Deployment time: 30 seconds No tokens. No secrets. No bullshit. "The simplest system that works." 🛣️ 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
439 lines
8.3 KiB
Markdown
439 lines
8.3 KiB
Markdown
# The REAL Deployment System - Pi Edition
|
|
|
|
**How we actually deploy everything. No GitHub Actions. No Cloudflare Pages. No bullshit.**
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Internet → Cloudflare DNS → Cloudflare Tunnel → Pi → Caddy → Docker
|
|
```
|
|
|
|
That's it.
|
|
|
|
---
|
|
|
|
## The Pi (192.168.4.64)
|
|
|
|
**Hardware:** Raspberry Pi 4B
|
|
**OS:** Raspberry Pi OS
|
|
**Hostname:** aria
|
|
**Running:** 26+ Docker containers
|
|
|
|
### Active Services
|
|
|
|
```bash
|
|
lucidia-earth → port 3040 (3D Metaverse)
|
|
blackroadinc-us → port 9444
|
|
blackroad-docs → port 3050
|
|
roadchain-io → port 3031
|
|
roadcoin-io → port 3030
|
|
blackroadqi-com → port 3020
|
|
earth-blackroad-io → port 3013
|
|
caddy → reverse proxy (handles all routing)
|
|
portainer → port 9000 (container management)
|
|
```
|
|
|
|
### The Cloudflare Tunnel
|
|
|
|
Running as system service:
|
|
```bash
|
|
/usr/bin/cloudflared tunnel run --token eyJhIjoiODQ4Y2YwYjE4ZDUxZTAxNzBlMGQxNTM3YWVjMzUwNWEiLCJ0IjoiNTI5MTU4NTktZGExOC00YWE2LWFkZDUtN2JkOWZjYWMyZTBiIiwicyI6Ik1HWXdaRE13TXpVdFpHSXdZaTAwTjJJNUxUZzRObVV0WWpOaE1XSmxNVGRsTXpSbSJ9
|
|
```
|
|
|
|
This creates an encrypted tunnel from Cloudflare's edge to the Pi. No port forwarding. No VPN. No firewall config.
|
|
|
|
---
|
|
|
|
## How to Deploy ANYTHING
|
|
|
|
### Step 1: Build it locally
|
|
|
|
```bash
|
|
cd ~/your-project
|
|
npm run build
|
|
# or
|
|
python -m build
|
|
# or whatever your build command is
|
|
```
|
|
|
|
### Step 2: Copy it to the Pi
|
|
|
|
```bash
|
|
tar czf dist.tar.gz dist/
|
|
scp dist.tar.gz pi@192.168.4.64:/tmp/
|
|
```
|
|
|
|
### Step 3: Run it in Docker
|
|
|
|
```bash
|
|
ssh pi@192.168.4.64
|
|
|
|
# Extract
|
|
cd /tmp
|
|
tar xzf dist.tar.gz
|
|
|
|
# Run container on any available port
|
|
docker run -d \
|
|
--name your-site \
|
|
--restart unless-stopped \
|
|
-p 3050:80 \
|
|
-v /tmp/dist:/usr/share/nginx/html:ro \
|
|
nginx:alpine
|
|
```
|
|
|
|
### Step 4: Add to Caddy
|
|
|
|
```bash
|
|
# Edit Caddyfile
|
|
docker exec caddy vi /etc/caddy/Caddyfile
|
|
|
|
# Add your domain:
|
|
your-domain.com {
|
|
reverse_proxy localhost:3050
|
|
}
|
|
|
|
# Reload
|
|
docker exec caddy caddy reload --config /etc/caddy/Caddyfile
|
|
```
|
|
|
|
### Step 5: Add DNS Record
|
|
|
|
Go to Cloudflare dashboard:
|
|
- Type: CNAME
|
|
- Name: your-domain.com
|
|
- Content: lucidia-earth.pages.dev (or the tunnel hostname)
|
|
- Proxied: Yes
|
|
|
|
**Done. That's the whole process.**
|
|
|
|
---
|
|
|
|
## Real Example: Deploying lucidia.earth Metaverse
|
|
|
|
### What we did:
|
|
|
|
```bash
|
|
# Local machine
|
|
cd ~/lucidia-metaverse
|
|
npm run build
|
|
tar czf dist.tar.gz dist/
|
|
scp dist.tar.gz pi@192.168.4.64:/tmp/
|
|
|
|
# On Pi
|
|
ssh pi@192.168.4.64
|
|
cd /tmp
|
|
tar xzf dist.tar.gz
|
|
docker stop lucidia-earth
|
|
docker rename lucidia-earth lucidia-earth-old
|
|
docker run -d \
|
|
--name lucidia-earth \
|
|
--restart unless-stopped \
|
|
-p 3040:80 \
|
|
-v /tmp/dist:/usr/share/nginx/html:ro \
|
|
nginx:alpine
|
|
|
|
# Test
|
|
curl http://localhost:3040
|
|
```
|
|
|
|
Caddy was already configured. DNS already pointed to the tunnel. Instant deployment.
|
|
|
|
**Time: 30 seconds.**
|
|
|
|
---
|
|
|
|
## Why This Works Better
|
|
|
|
### GitHub Actions / Cloudflare Pages
|
|
- ❌ Requires API tokens (expire, get revoked, auth errors)
|
|
- ❌ Requires GitHub secrets management
|
|
- ❌ Requires workflow files
|
|
- ❌ Requires SHA pinning for security
|
|
- ❌ Runs on shared infrastructure (slow, unreliable)
|
|
- ❌ 5-10 minute build times
|
|
- ❌ Complex debugging when it breaks
|
|
- ❌ 58 repositories = 58 configurations
|
|
|
|
### Pi Direct Deployment
|
|
- ✅ No tokens, no secrets, no auth
|
|
- ✅ No config files needed
|
|
- ✅ Runs on dedicated hardware (fast, reliable)
|
|
- ✅ 30 second deployments
|
|
- ✅ Direct SSH access for debugging
|
|
- ✅ One Pi = all 58 sites
|
|
- ✅ Can test locally before deploying
|
|
- ✅ Full control over everything
|
|
|
|
---
|
|
|
|
## Container Management
|
|
|
|
### View all containers
|
|
```bash
|
|
ssh pi@192.168.4.64
|
|
docker ps
|
|
```
|
|
|
|
### View logs
|
|
```bash
|
|
docker logs lucidia-earth --tail 50 -f
|
|
```
|
|
|
|
### Restart a site
|
|
```bash
|
|
docker restart lucidia-earth
|
|
```
|
|
|
|
### Update a site
|
|
```bash
|
|
# Build new version locally
|
|
npm run build
|
|
|
|
# Deploy
|
|
scp -r dist/* pi@192.168.4.64:/tmp/dist/
|
|
ssh pi@192.168.4.64 "docker restart lucidia-earth"
|
|
```
|
|
|
|
### Rollback
|
|
```bash
|
|
docker stop lucidia-earth
|
|
docker rename lucidia-earth-old lucidia-earth
|
|
docker start lucidia-earth
|
|
```
|
|
|
|
---
|
|
|
|
## Caddy Configuration
|
|
|
|
Location: `/etc/caddy/Caddyfile` (inside caddy container)
|
|
|
|
### Current routing:
|
|
|
|
```caddyfile
|
|
# Main lucidia.earth
|
|
lucidia.earth, www.lucidia.earth {
|
|
reverse_proxy localhost:3040
|
|
}
|
|
|
|
# BlackRoad main site
|
|
blackroad.io {
|
|
reverse_proxy localhost:3000
|
|
}
|
|
|
|
# Subdomains
|
|
earth.blackroad.io {
|
|
reverse_proxy localhost:3013
|
|
}
|
|
|
|
demo.blackroad.io {
|
|
reverse_proxy localhost:3002
|
|
}
|
|
|
|
# Quantum domains
|
|
blackroadqi.com {
|
|
reverse_proxy localhost:3020
|
|
}
|
|
|
|
# Blockchain
|
|
roadchain.io {
|
|
reverse_proxy localhost:3031
|
|
}
|
|
|
|
roadcoin.io {
|
|
reverse_proxy localhost:3030
|
|
}
|
|
|
|
# Future Lucidia subdomains
|
|
app.lucidia.earth {
|
|
reverse_proxy localhost:3041
|
|
}
|
|
|
|
tube.lucidia.earth {
|
|
reverse_proxy localhost:3042
|
|
}
|
|
|
|
studio.lucidia.earth {
|
|
reverse_proxy localhost:3043
|
|
}
|
|
```
|
|
|
|
Caddy automatically handles:
|
|
- SSL certificates (Let's Encrypt)
|
|
- HTTP → HTTPS redirect
|
|
- Certificate renewal
|
|
- Load balancing (if multiple backends)
|
|
|
|
---
|
|
|
|
## Port Allocation
|
|
|
|
Reserved ports on the Pi:
|
|
|
|
```
|
|
3000 - blackroad.io (main site)
|
|
3001 - node-api-3001
|
|
3002 - demo.blackroad.io
|
|
3003 - node-api-3003
|
|
3004 - creator-studio.blackroad.io
|
|
3005 - devops.blackroad.io
|
|
3006 - education.blackroad.io
|
|
3007 - finance.blackroad.io
|
|
3008 - ideas.blackroad.io
|
|
3009 - legal.blackroad.io
|
|
3010 - research-lab.blackroad.io
|
|
3011 - studio.blackroad.io
|
|
3012 - brand.blackroad.io
|
|
3013 - earth.blackroad.io
|
|
3020 - blackroadqi.com
|
|
3021 - blackroadquantum.info
|
|
3022 - blackroadquantum.net
|
|
3023 - blackroadquantum.shop
|
|
3024 - blackroadquantum.store
|
|
3030 - roadcoin.io
|
|
3031 - roadchain.io
|
|
3040 - lucidia.earth (3D Metaverse)
|
|
3041 - app.lucidia.earth (reserved)
|
|
3042 - tube.lucidia.earth (reserved)
|
|
3043 - studio.lucidia.earth (reserved)
|
|
3044 - (available)
|
|
3050 - blackroad-docs
|
|
8081 - blackroad-whoami
|
|
9000 - portainer
|
|
9443 - portainer (https)
|
|
9444 - blackroadinc.us
|
|
```
|
|
|
|
Next available: 3044+
|
|
|
|
---
|
|
|
|
## Deployment Automation Script
|
|
|
|
Save as `~/deploy-to-pi.sh`:
|
|
|
|
```bash
|
|
#!/usr/bin/env bash
|
|
# deploy-to-pi.sh - Deploy any project to the Pi
|
|
# Usage: ./deploy-to-pi.sh <project-name> <port> <domain>
|
|
|
|
set -e
|
|
|
|
PROJECT=$1
|
|
PORT=$2
|
|
DOMAIN=$3
|
|
|
|
if [ -z "$PROJECT" ] || [ -z "$PORT" ] || [ -z "$DOMAIN" ]; then
|
|
echo "Usage: ./deploy-to-pi.sh <project-name> <port> <domain>"
|
|
echo "Example: ./deploy-to-pi.sh lucidia-metaverse 3040 lucidia.earth"
|
|
exit 1
|
|
fi
|
|
|
|
echo "🚀 Deploying $PROJECT to $DOMAIN on port $PORT..."
|
|
|
|
# Build
|
|
echo "📦 Building..."
|
|
npm run build
|
|
|
|
# Package
|
|
echo "📦 Packaging..."
|
|
tar czf /tmp/$PROJECT.tar.gz dist/
|
|
|
|
# Upload
|
|
echo "📤 Uploading to Pi..."
|
|
scp /tmp/$PROJECT.tar.gz pi@192.168.4.64:/tmp/
|
|
|
|
# Deploy
|
|
echo "🐳 Deploying container..."
|
|
ssh pi@192.168.4.64 << ENDSSH
|
|
cd /tmp
|
|
tar xzf $PROJECT.tar.gz
|
|
docker stop $PROJECT 2>/dev/null || true
|
|
docker rm $PROJECT 2>/dev/null || true
|
|
docker run -d \
|
|
--name $PROJECT \
|
|
--restart unless-stopped \
|
|
-p $PORT:80 \
|
|
-v /tmp/dist:/usr/share/nginx/html:ro \
|
|
nginx:alpine
|
|
echo "✅ Container running on port $PORT"
|
|
ENDSSH
|
|
|
|
echo ""
|
|
echo "✅ DEPLOYED!"
|
|
echo " Container: $PROJECT"
|
|
echo " Port: $PORT"
|
|
echo " URL: https://$DOMAIN"
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. Add domain to Caddy config"
|
|
echo " 2. Add DNS record in Cloudflare"
|
|
|
|
rm /tmp/$PROJECT.tar.gz
|
|
```
|
|
|
|
Usage:
|
|
```bash
|
|
chmod +x ~/deploy-to-pi.sh
|
|
~/deploy-to-pi.sh lucidia-metaverse 3040 lucidia.earth
|
|
```
|
|
|
|
---
|
|
|
|
## Monitoring
|
|
|
|
### Check Pi status
|
|
```bash
|
|
ssh pi@192.168.4.64 "
|
|
echo 'System:'
|
|
uptime
|
|
echo ''
|
|
echo 'Containers:'
|
|
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
|
|
echo ''
|
|
echo 'Caddy status:'
|
|
docker logs caddy --tail 5
|
|
"
|
|
```
|
|
|
|
### Check tunnel status
|
|
```bash
|
|
ssh pi@192.168.4.64 "ps aux | grep cloudflared"
|
|
```
|
|
|
|
### Check site is live
|
|
```bash
|
|
curl -s https://lucidia.earth | grep -o '<title>.*</title>'
|
|
```
|
|
|
|
---
|
|
|
|
## Backups
|
|
|
|
All container volumes are stored in `/var/lib/docker/volumes/` on the Pi.
|
|
|
|
To backup a site:
|
|
```bash
|
|
ssh pi@192.168.4.64
|
|
docker commit lucidia-earth lucidia-earth:backup-$(date +%Y%m%d)
|
|
docker save lucidia-earth:backup-$(date +%Y%m%d) | gzip > ~/backups/lucidia-earth-$(date +%Y%m%d).tar.gz
|
|
```
|
|
|
|
---
|
|
|
|
## The Truth
|
|
|
|
This is how we **actually** deploy everything. Not GitHub Actions. Not Cloudflare Pages. Just:
|
|
|
|
1. Build locally
|
|
2. SCP to Pi
|
|
3. Docker run
|
|
4. Done
|
|
|
|
**30 seconds. Every time. No exceptions.**
|
|
|
|
---
|
|
|
|
**"The simplest system that works." 🛣️**
|