Configuration and deployment files for hosting multiple client sites on a single Digital Ocean droplet, each with a scraped static website and a connected AnythingLLM chat widget.
Each client gets:
- A static website (scraped web pages) served via nginx, accessible at
clientname.yourdomain.com.au - An AnythingLLM chat widget embedded on the site, powered by a shared AnythingLLM instance that provides RAG-based Q&A about the client's content
Traffic flows through:
Route53 (NS → Cloudflare) → Cloudflare (proxy/DDOS) → DO Reserved IP → Caddy → Containers
All services run as Docker containers on a single droplet, connected via a shared caddy-net network:
- Caddy - Reverse proxy handling TLS termination with Cloudflare origin certificates and routing subdomains to the correct containers
- Client sites - nginx containers serving static HTML (one per client)
- AnythingLLM - RAG/chat instances (one instance per ~3 clients)
/srv/ # Config and code (on droplet)
├── caddy/
│ ├── docker-compose.yml
│ ├── Caddyfile
│ └── certs/
│ ├── origin.pem
│ └── origin-key.pem
├── sites/
│ ├── client1/ # Cloned from GitHub
│ │ ├── docker-compose.yml
│ │ └── html/
│ ├── client2/
│ └── client3/
└── anythingllm/
└── instance1/
└── docker-compose.yml
<VOLUME_PATH>/ # Persistent data (DO attached volume)
├── caddy/ # Caddy TLS state
└── anythingllm/
└── instance1/ # AnythingLLM database + uploads
| Path | Purpose |
|---|---|
caddy/docker-compose.yml |
Caddy reverse proxy container config |
caddy/Caddyfile |
Routing rules (subdomain → container) |
sites/client*/docker-compose.yml |
Template docker-compose for nginx client sites |
anythingllm/instance1/docker-compose.yml |
AnythingLLM container config |
tutorial.html |
Interactive step-by-step setup guide (open in browser) |
PLAN.md |
Detailed deployment plan with all commands |
Open tutorial.html in your browser for an interactive 18-step walkthrough that covers:
- Creating the droplet and reserved IP
- Setting up the directory structure and SSH keys
- Configuring Cloudflare DNS and SSL certificates
- Deploying Caddy, client sites, and AnythingLLM
- Adding the chat widget to client sites
- Verification and firewall setup
The tutorial lets you enter your specific values (IP, domain, subdomains, volume path) and interpolates them into all commands, so you can copy-paste directly.
- Digital Ocean account
- Cloudflare account (free tier)
- Domain registered via AWS Route53
- Client site repos on GitHub (private, SSH access)
- Use the Website Snapshot project to download a copy of the site in question.
- This will download and save a copy of the site as well as creating the dockerfile and docker-compose files.
- SSH into the server and clone the new site repo to
/srv/sites/on the droplet. - Start the container with
docker compose up -d - Navigate tot he Caddyfile and add a new proxy block pointing to the new container/service name.
- Reload Caddy:
docker exec caddy caddy reload --config /etc/caddy/Caddyfile - Add a DNS record in Cloudflare (A record, proxied)
ssh root@<YOUR_RESERVED_IP>
cd /srv/sites/client1
git pullSince HTML is mounted as a volume, nginx serves the updated files immediately with no container restart needed.