DEV Community

Cover image for I Built My Own Private Cloud + 4 AI Assistants on One Server — Building Your Private AI Infrastructure [3/5]
kusunoki
kusunoki

Posted on • Edited on

I Built My Own Private Cloud + 4 AI Assistants on One Server — Building Your Private AI Infrastructure [3/5]

Free series. All open-source. No DevOps background required. Estimated hands-on time: 60–90 minutes.


You have a hardened VPS, two open ports, and a Cloudflare tunnel that makes your server invisible to the public internet. The walls are up. Now let's put something worth protecting inside them.

This part installs Docker, PostgreSQL, Nextcloud, Collabora Online, a unified 4-model AI proxy, and OpenClaw — your agentic butler — plus CalDAV calendar sync, custom domain email, and nightly backups to Supabase. By the end, the system is operational for daily use.

Every step follows the same pattern as Part 2: paste the command, observe the result, verify before proceeding. If something breaks, you know exactly where and how to fix it.


What You Will Complete in This Part

Read time: ~20 min. Hands-on: ~60–90 min. Prerequisites: Parts 1–2 completed. Four API keys gathered. Password manager open.

When this part is complete, eleven things will be true:

  1. Docker installed and running
  2. PostgreSQL containerized, holding Nextcloud data
  3. Nextcloud live at cloud.yourdomain.com, behind Cloudflare Access
  4. Collabora integrated — .docx/.xlsx open in-browser for real-time co-editing
  5. LLM proxy at ai.yourdomain.com, routing to 4 AI providers, keys in isolated .env
  6. OpenClaw installed, systemd-sandboxed, connected to your messaging app
  7. CalDAV sync active on all devices
  8. Custom domain email configured with DKIM/SPF/DMARC
  9. Supabase backup running nightly at 2 AM
  10. All 8 security layers active
  11. Monthly cost fully understood — no line surprises

Before You Build: Four API Keys

Collect all keys before touching the terminal:

OPENAI_API_KEY=sk-[your key]
ANTHROPIC_API_KEY=sk-ant-[your key]
GOOGLE_API_KEY=AIza-[your key]
PERPLEXITY_API_KEY=pplx-[your key]
Enter fullscreen mode Exit fullscreen mode

Spending rule: set a hard $20/month cap per provider before your first API request. Total exposure: $80. Realistic spend for a 3-person team: $15–$35/month.

OpenAI — platform.openai.com. Broad reasoning, coding, general analysis. Anthropic — console.anthropic.com. Nuanced prose, contracts, editorial. Google — aistudio.google.com. Structured data, Workspace integration. Perplexity — perplexity.ai. Source-cited real-time research.


Step 1: Docker and PostgreSQL

Log in as myadmin.

sudo apt install -y docker.io docker-compose
sudo systemctl enable --now docker
sudo usermod -aG docker myadmin
Enter fullscreen mode Exit fullscreen mode

Restart session: exit, log back in. Not optional — Docker commands fail without the group reload.

docker --version
docker ps
Enter fullscreen mode Exit fullscreen mode

docker --version: 24.x.x+. docker ps: empty table (correct — no containers yet).


Step 2: The Compose Blueprint

Docker Compose manages the entire stack from one YAML file.

mkdir -p ~/nextcloud && cd ~/nextcloud
nano docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

Paste exactly. Three substitutions required:

version: '3'
services:
  db:
    image: postgres:15
    restart: always
    environment:
      - POSTGRES_DB=nextcloud_db
      - POSTGRES_USER=nextcloud_user
      - POSTGRES_PASSWORD=REPLACE_WITH_STRONG_PASSWORD
    volumes:
      - nextcloud_db_data:/var/lib/postgresql/data

  app:
    image: nextcloud:latest
    restart: always
    depends_on:
      - db
    ports:
      - "127.0.0.1:8080:80"
    environment:
      - POSTGRES_HOST=db
      - POSTGRES_DB=nextcloud_db
      - POSTGRES_USER=nextcloud_user
      - POSTGRES_PASSWORD=REPLACE_WITH_SAME_PASSWORD
    volumes:
      - nextcloud_data:/var/www/html

  collabora:
    image: collabora/code:latest
    restart: always
    environment:
      - aliasgroup1=https://cloud.REPLACE_WITH_YOUR_DOMAIN:443
      - extra_params=--o:ssl.enable=false --o:ssl.termination=true
    ports:
      - "127.0.0.1:9980:9980"

volumes:
  nextcloud_db_data:
  nextcloud_data:
Enter fullscreen mode Exit fullscreen mode

Substitutions: (1) REPLACE_WITH_STRONG_PASSWORD — same 20+ char password in both locations. (2) REPLACE_WITH_YOUR_DOMAIN — your actual domain.

Save: Ctrl+O → Enter → Ctrl+X.

docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

First run pulls all images (~5–10 min). Verify:

docker ps
Enter fullscreen mode Exit fullscreen mode

Three containers with Up status: PostgreSQL, Nextcloud, Collabora.

Browse to https://cloud.yourdomain.com. Cloudflare OTP → Nextcloud setup screen. Create admin account. Store credentials.

When the dashboard loads, your private cloud is operational.


Step 3: Collabora Online

Nextcloud → user icon → Apps → search "Nextcloud Office" → Install.

User icon → Administration Settings → Nextcloud Office → Use your own server → URL: https://office.yourdomain.com → Save.

Verify: + button → New Document → editor opens in browser. Open from second device → both cursors visible, edits sync in real time.


Step 4: The 4-Model AI Proxy

Runs at localhost:8000, routed to ai.yourdomain.com via Cloudflare Tunnel. Keys never reach the browser.

mkdir -p ~/llm-proxy && cd ~/llm-proxy
python3 -m venv venv
source venv/bin/activate
pip install fastapi uvicorn httpx python-dotenv
Enter fullscreen mode Exit fullscreen mode
nano ~/llm-proxy/.env
Enter fullscreen mode Exit fullscreen mode

Paste with your actual keys:

OPENAI_API_KEY=sk-your-openai-key
ANTHROPIC_API_KEY=sk-ant-your-anthropic-key
GOOGLE_API_KEY=AIza-your-google-key
PERPLEXITY_API_KEY=pplx-your-perplexity-key
PROXY_AUTH_TOKEN=REPLACE_WITH_RANDOM_TOKEN
Enter fullscreen mode Exit fullscreen mode

Generate auth token:

python3 -c "import secrets; print(secrets.token_urlsafe(32))"
Enter fullscreen mode Exit fullscreen mode

Paste output as PROXY_AUTH_TOKEN value. Save.

chmod 600 ~/llm-proxy/.env
Enter fullscreen mode Exit fullscreen mode

Full proxy application code is in the Part 5 operations file. Verify:

curl -s http://localhost:8000/health
Enter fullscreen mode Exit fullscreen mode

Expected: {"status":"ok"}. Confirm at https://ai.yourdomain.com/health.

Model allocation: Claude for contracts/editorial. Perplexity for cited research. ChatGPT for general reasoning. Gemini for structured data. One portal, right model per task.


Step 5: OpenClaw — Full Install

A chatbot answers and waits. An agent acts and completes.

CVE-2026-25253 (CVSS 8.8, High) and the ClawJacked class were addressed in Part 1. The security posture from Part 2 — localhost binding, Cloudflare tunnel auth, UFW port restriction — neutralizes both structurally. This install specifies OpenClaw 2026.1.29 or later (patched).

npm install -g openclaw@latest
openclaw onboard --install-daemon
Enter fullscreen mode Exit fullscreen mode

Interactive flow: select AI model (Claude/ChatGPT), configure working dirs, connect messaging app (Slack/Discord/Telegram).

Three mandatory configs before production:

  1. Set OpenClaw's internal spending cap (separate from provider caps).
  2. Restrict filesystem access — exclude dirs containing passwords, keys, financial records.
  3. No third-party skills without review — community extensions are unaudited.
openclaw status
Enter fullscreen mode Exit fullscreen mode

Expected: "Gateway: running." Test via messaging app.


Step 6: CalDAV Calendar Sync

iPhone: Settings → Calendar → Accounts → Add → Other → CalDAV. Server: https://cloud.yourdomain.com/remote.php/dav.

Android: DAVx⁵ (free, Play Store). Same server + credentials.

Mac/PC: Apple Calendar or Thunderbird — CalDAV natively supported.

Test: create event in Nextcloud → appears on phone within minutes.


Step 7: Custom Domain Email

Cloudflare Email Routing: Email → Email Routing → Enable. Forward yourname@yourdomain.com to your existing inbox.

Send-as config: Gmail → Settings → Accounts and Import → Add another address. Apple Mail → Preferences → Accounts → alias.

DMARC: Cloudflare DNS → TXT record:

Name: _dmarc
Content: v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com
Enter fullscreen mode Exit fullscreen mode

Verify at mail-tester.com. Score ≥8 = reliable inbox delivery.


Step 8: Supabase Nightly Backup

Create project at supabase.com. Region: US East. Copy connection string.

nano ~/.supabase-backup.conf
Enter fullscreen mode Exit fullscreen mode
SUPABASE_URI="postgresql://postgres.[project-id]:[password]@aws-0-us-east-1.pooler.supabase.com:6543/postgres"
LOCAL_DB_NAME="nextcloud_db"
LOCAL_DB_USER="nextcloud_user"
LOCAL_DB_CONTAINER="nextcloud_db_1"
Enter fullscreen mode Exit fullscreen mode
chmod 600 ~/.supabase-backup.conf
Enter fullscreen mode Exit fullscreen mode
nano ~/db-backup-to-supabase.sh
Enter fullscreen mode Exit fullscreen mode

Paste (change myadmin if your username differs):

#!/bin/bash
set -euo pipefail
source /home/myadmin/.supabase-backup.conf
DATE=$(date +%Y%m%d_%H%M%S)
DUMP_DIR="/home/myadmin/db-backups"
DUMP_FILE="${DUMP_DIR}/nextcloud_db_${DATE}.sql"
mkdir -p "$DUMP_DIR"
echo "[$(date)] Backup started"
docker exec "$LOCAL_DB_CONTAINER" pg_dump -U "$LOCAL_DB_USER" "$LOCAL_DB_NAME" > "$DUMP_FILE"
echo "[$(date)] Local dump complete: $DUMP_FILE"
PGPASSWORD=$(echo "$SUPABASE_URI" | grep -oP '(?<=:)[^@]+(?=@)')
psql "$SUPABASE_URI" -c "DROP SCHEMA IF EXISTS nextcloud_backup CASCADE; CREATE SCHEMA nextcloud_backup;"
psql "$SUPABASE_URI" --set search_path=nextcloud_backup < "$DUMP_FILE" 2>/dev/null || true
echo "[$(date)] Transfer to Supabase complete"
find "$DUMP_DIR" -name "*.sql" -mtime +14 -delete
echo "[$(date)] Backup complete"
Enter fullscreen mode Exit fullscreen mode
chmod 700 ~/db-backup-to-supabase.sh
~/db-backup-to-supabase.sh
Enter fullscreen mode Exit fullscreen mode

Confirm in Supabase: Table Editor → nextcloud_backup schema exists.

crontab -e
Enter fullscreen mode Exit fullscreen mode

Add:

0 2 * * * /home/myadmin/db-backup-to-supabase.sh >> /home/myadmin/db-backup.log 2>&1
Enter fullscreen mode Exit fullscreen mode

Recovery (total server loss):

pg_dump "your-supabase-uri" --schema=nextcloud_backup > /tmp/restore.sql
docker exec -i nextcloud_db_1 psql -U nextcloud_user nextcloud_db < /tmp/restore.sql
Enter fullscreen mode Exit fullscreen mode

Three Rules of AI Use

1. No PII in prompts. Names, SSNs, financial accounts, medical data — none of it belongs in an AI prompt. Use initials or pseudonyms. CCPA and HIPAA don't distinguish accidental from intentional disclosure.

2. No credentials in chat. Keys are in .env. Passwords are in Bitwarden. They never need to appear in a message.

3. Review every output before it acts. Claude writes excellent email. Read it first. OpenClaw organizes files as instructed. Check the result. The irreversible consequences of automated action belong to the human who gave the instruction.


Verification Checklist

docker ps
Enter fullscreen mode Exit fullscreen mode

Expected: 3 containers Up.

Browser: https://cloud.yourdomain.com → Nextcloud loads. Open .docx → Collabora editor.

curl -s http://localhost:8000/health
Enter fullscreen mode Exit fullscreen mode

{"status":"ok"} — also at https://ai.yourdomain.com/health.

openclaw status
Enter fullscreen mode Exit fullscreen mode

"Gateway: running." Message via app → response arrives.

Calendar event in Nextcloud → phone. Email to custom domain → forwarding inbox. Reply → sender shows custom address.

sudo ufw status verbose
Enter fullscreen mode Exit fullscreen mode

Active, 80+443 only.

sudo systemctl status fail2ban cloudflared
Enter fullscreen mode Exit fullscreen mode

Both active.

~/db-backup-to-supabase.sh
Enter fullscreen mode Exit fullscreen mode

Completes. Supabase shows data.

All twelve checked: system is fully operational for daily use.


Monthly Cost

VPS: $12–$48. Domain: ~$1. Cloudflare: $0. Supabase: $0. Software: $0. AI: $15–$35 (moderate, 3 users).

Total (3–8 person team): ~$35–$50/month. Equivalent SaaS for 3 users: $240/month AI alone, plus storage, monitoring, remote desktop — $400+ total.


What Part 4 Builds

Apache Guacamole — browser-based remote desktop. Prometheus + Grafana + Alertmanager — real-time monitoring with email alerts. AES-256 encrypted weekly backups — the third layer of defense.

When Part 4 is complete, the build is finished.


Series: Building Your Private AI Infrastructure

Part What It Covers
Part 1 — Architecture Overview Stack, costs, security model
Part 2 — Zero-Trust Server Vultr, Cloudflare, UFW, fail2ban
Part 3 — The Intelligence Layer (you are here) Docker, Nextcloud, Collabora, AI proxy, OpenClaw
Part 4 — Operations & Monitoring Guacamole, Prometheus, Grafana, encrypted backups
Part 5 — The Operations Manual Maintenance, audits, cost optimization, runbook

All five parts are published and free.


Legal Disclaimer

The information provided in this series is for educational and informational purposes only. It does not constitute legal, financial, tax, accounting, cybersecurity, or professional advice. All use is at the sole risk of the user. To the maximum extent permitted by applicable law, including the laws of the State of California (Cal. Civ. Code §§1668, 3513) and the State of New York (N.Y. GOL §5-323), the author disclaims all liability for any damages arising from use of this content. References to all third-party products are for informational purposes only. The author has no commercial relationship with any provider mentioned.


Part 4 is next. Questions, errors, environment-specific issues: comments. Every one gets read. Technical ones get detailed answers.

— Kusunoki
International Tax Specialist & Systems Builder
Sapporo, Japan | @kusunoki

Top comments (0)