
LibreChat gives you a private, ChatGPT-style web UI you control. Pair it with Ollama to serve open models from your own server. That keeps prompts out of third-party SaaS while still giving your team a UI they’ll actually use.
This walkthrough shows how to self-host LibreChat with Ollama on a VPS using Docker Compose. You’ll also put it behind HTTPS and disable open registration.
The examples assume Ubuntu Server on a VPS or dedicated host. You’ll need a public IP and a domain name.
If you’d rather not spend time on OS patching, backups, and baseline hardening, managed VPS hosting keeps the platform chores off your plate. If you want full control, start with a HostMyCode VPS and follow the steps as written.
What you’ll build (and what it won’t do)
By the end, you’ll have:
- LibreChat running in Docker, reachable at
https://chat.yourdomain.com - Ollama running locally on the same VPS (or a separate host), serving models to LibreChat
- HTTPS via Let’s Encrypt
- User registration disabled (invite-only) so random visitors can’t create accounts
Honest limitations:
- CPU-only inference is slow. It works for light internal use and smaller models, but it won’t feel like a premium hosted model under load.
- VRAM sets your ceiling if you use a GPU. On CPU, RAM and patience set your ceiling.
- This is not an isolation boundary. Treat LibreChat and Ollama as internal services. Use TLS, require auth, and don’t expose Ollama directly to the internet.
Prerequisites and sizing (CPU/RAM/disk)
Minimum workable baseline for a small team on CPU-only:
- vCPU: 4 cores
- RAM: 8–16 GB (16 GB is noticeably smoother)
- Disk: 80+ GB SSD/NVMe (models take space fast)
- OS: Ubuntu Server LTS
For regular daily use on CPU, 8 vCPU / 32 GB RAM is a more comfortable baseline.
If you want faster responses with larger models, plan on a GPU VPS or a dedicated server with a supported NVIDIA GPU.
HostMyCode offers both scalable VPS plans and larger machines via dedicated servers once a single VM stops being enough.
You’ll also need:
- A domain pointing to your VPS (example:
chat.yourdomain.com) - Ports open: 80/tcp and 443/tcp to the reverse proxy
Step 1: Prepare the VPS (packages, firewall, Docker)
SSH in as a sudo-capable user.
sudo apt update
sudo apt -y upgrade
sudo apt -y install ca-certificates curl gnupg ufw
Set a basic firewall policy.
If UFW is new to you, follow this HostMyCode walkthrough and then come back: set up UFW firewall on Ubuntu for a web server.
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable
sudo ufw status
Install Docker Engine and the Compose plugin. Ubuntu’s packages are fine for this stack.
sudo apt -y install docker.io docker-compose-plugin
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
newgrp docker
Quick check:
docker version
docker compose version
Step 2: Create DNS records for your chat subdomain
Create an A record for chat.yourdomain.com pointing to your VPS public IP.
If you manage domains at HostMyCode, do it from domains. Use a TTL of 300 seconds while you’re building and testing.
If you’re also moving an existing site and want a clean cutover, use a low-TTL/parallel-hosting approach. This runbook is a good reference: DNS cutover tutorial for a low-TTL migration.
Step 3: Deploy Ollama (local model server)
You’ll run Ollama as a container. LibreChat will talk to it over the Docker network.
Because of that, you don’t need to publish Ollama to the internet.
Create a working directory:
mkdir -p ~/librechat-ollama
cd ~/librechat-ollama
Create an Ollama volume so models persist:
docker volume create ollama
Pull and start Ollama:
docker run -d \
--name ollama \
--restart unless-stopped \
-v ollama:/root/.ollama \
-p 127.0.0.1:11434:11434 \
ollama/ollama
Binding to 127.0.0.1:11434 keeps Ollama off the public interface.
In Step 5, you’ll likely move Ollama into the same Compose project. That lets LibreChat reach it by container name.
Sanity check:
curl -s http://127.0.0.1:11434/api/tags | head
Now pull a model. On CPU, start small.
Get the stack working end-to-end first. Then try larger models.
- Fast CPU test: a small instruct model
- Heavier: 7B–8B class models (better quality, slower on CPU)
docker exec -it ollama ollama pull llama3.2:3b
If that model name isn’t available on your system, list what Ollama has after you pull something:
docker exec -it ollama ollama list
Useful diagnostics if inference feels brutally slow:
docker stats --no-stream
free -h
lscpu | egrep 'Model name|CPU\(s\)|Thread|Core'
Step 4: Deploy LibreChat with Docker Compose
LibreChat typically stores state in MongoDB.
In this setup, you’ll run LibreChat + MongoDB + a reverse proxy for TLS on one host. That maps cleanly to a VPS.
Create directories:
cd ~/librechat-ollama
mkdir -p librechat
cd librechat
Create a .env file for LibreChat. Upstream settings change over time, so keep this file focused.
Set your public URL, closed signup, and MongoDB connection.
cat > .env <<'EOF'
# Public URL (must match your TLS hostname)
DOMAIN=chat.yourdomain.com
# LibreChat app settings
ALLOW_REGISTRATION=false
ALLOW_EMAIL_LOGIN=true
# Use strong secrets (generate below)
SESSION_SECRET=
JWT_SECRET=
CREDS_KEY=
CREDS_IV=
# MongoDB
MONGO_URI=mongodb://mongo:27017/librechat
EOF
Generate secrets (this prints values you’ll paste into .env):
python3 - <<'PY'
import secrets
print('SESSION_SECRET=' + secrets.token_hex(32))
print('JWT_SECRET=' + secrets.token_hex(32))
print('CREDS_KEY=' + secrets.token_hex(32))
print('CREDS_IV=' + secrets.token_hex(16))
PY
Edit .env and paste the generated values:
nano .env
Now create a docker-compose.yml with three services:
- mongo for LibreChat data
- librechat as the web app
- caddy as the reverse proxy that automatically gets/renews TLS
cat > docker-compose.yml <<'EOF'
services:
mongo:
image: mongo:7
container_name: librechat-mongo
restart: unless-stopped
volumes:
- mongo_data:/data/db
librechat:
image: ghcr.io/danny-avila/librechat:latest
container_name: librechat
restart: unless-stopped
env_file:
- .env
depends_on:
- mongo
volumes:
- librechat_data:/app/client/public/uploads
environment:
- MONGO_URI=${MONGO_URI}
caddy:
image: caddy:2
container_name: librechat-caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
depends_on:
- librechat
volumes:
mongo_data:
librechat_data:
caddy_data:
caddy_config:
EOF
Create a Caddyfile for TLS and reverse proxy:
cat > Caddyfile <<EOF
${DOMAIN} {
encode zstd gzip
# Basic hardening headers
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
reverse_proxy librechat:3080
}
EOF
Bring the stack up:
docker compose up -d
docker compose ps
If anything looks unhealthy, go straight to the app logs:
docker compose logs --tail=200 -f librechat
If you prefer Nginx instead of Caddy, this HostMyCode reverse proxy tutorial lays out the same building blocks (service ports, upstream routing, TLS): set up Caddy reverse proxy for Docker on a VPS. (It’s Caddy-focused, but the patterns translate cleanly.)
Step 5: Connect LibreChat to Ollama (providers and base URL)
LibreChat can talk to multiple model backends. For Ollama, the simplest setup is to keep both containers on the same Compose network.
Once they share a network, reference Ollama by container name.
Add Ollama to the same Compose project. This avoids host-only bindings and keeps connectivity consistent.
Edit docker-compose.yml and add this service under services: (or replace your existing standalone container approach):
ollama:
image: ollama/ollama
container_name: ollama
restart: unless-stopped
volumes:
- ollama:/root/.ollama
Then add a volume at the bottom:
ollama:
Your volumes block should include:
volumes:
mongo_data:
librechat_data:
caddy_data:
caddy_config:
ollama:
Apply changes:
docker compose up -d
docker compose ps
Next, point LibreChat at Ollama. If your LibreChat build exposes an Ollama provider in the UI, use:
- Base URL:
http://ollama:11434
If the UI doesn’t expose the setting, you can mount a provider config file into the container. File names and paths vary across releases.
The most reliable operator workflow is still:
- Open LibreChat at
https://chat.yourdomain.com - Create the first admin user (on fresh installs, the first account is typically admin)
- In Admin/Settings, add the Ollama endpoint and select a model you’ve pulled
Pull at least one model inside the Ollama service:
docker exec -it ollama ollama pull llama3.2:3b
docker exec -it ollama ollama list
Quick internal connectivity test from the LibreChat container:
docker exec -it librechat sh -lc 'apk add --no-cache curl 2>/dev/null || true; curl -s http://ollama:11434/api/tags | head'
If that call fails, Ollama is probably still outside the Compose project.
Another common cause is binding Ollama to 127.0.0.1 without a shared network.
The clean fix is to run Ollama as a Compose service, as shown above.
Step 6: Make signup invite-only and keep access tight
Public AI chat endpoints get hammered. Even with login required, bots will keep trying to create accounts.
That burns CPU and can fill disk.
You already set ALLOW_REGISTRATION=false in .env. Verify the running container sees it:
docker exec -it librechat printenv | egrep 'ALLOW_REGISTRATION|DOMAIN|MONGO_URI'
Recommended additional controls:
- Keep Ollama private. Don’t publish port 11434 to the public internet.
- Rate-limit at the edge. If you later switch from Caddy to Nginx, add request limiting rules. This guide shows safe patterns: Nginx rate limiting for bots and brute-force.
- Use a VPN for admin access. For teams, WireGuard is a clean way to narrow access without fighting IP allowlists: set up WireGuard on an Ubuntu VPS.
Step 7: Add basic monitoring and capacity checks
Self-hosted AI usually fails in predictable ways. Disk fills with model layers, RAM pressure triggers OOM kills, or latency spikes during routine maintenance.
At minimum, keep an eye on CPU, RAM, and disk:
df -h
sudo journalctl -u docker --since "1 hour ago" --no-pager | tail -n 200
docker stats
If you want a lightweight dashboard plus alerting, Netdata is a practical choice. This HostMyCode guide walks through a clean setup: server monitoring with Netdata + uptime checks.
Step 8: Backups you can actually restore
LibreChat’s state lives mainly in MongoDB plus the uploads directory you mounted.
Back up both. Then test a restore on a throwaway VM before you trust it.
Create a backups directory:
mkdir -p ~/backups/librechat
cd ~/backups/librechat
Dump MongoDB (from the running container):
docker exec librechat-mongo mongodump --archive --gzip --db librechat > mongo-libreport-$(date +%F).archive.gz
Back up uploads (if you use the default named volume, tar it from a temporary container):
docker run --rm \
-v librechat_librechat_data:/data:ro \
-v $PWD:/backup \
ubuntu:24.04 bash -lc \
'cd /data && tar -czf /backup/librechat-uploads-$(date +%F).tar.gz .'
Store backups offsite. If you already have a general VPS backup process, include these two files in it.
What matters is a restore path you’ve practiced, not a cron job that “probably works.”
Troubleshooting checklist (common failure modes)
- 502/504 at the browser: Check
docker compose logs -f caddyand confirm LibreChat listens on port 3080 inside the network. - LibreChat loads, but no models appear: Confirm Ollama has at least one model:
docker exec -it ollama ollama list. - Ollama is reachable from host but not from LibreChat: Run Ollama as a Compose service so
http://ollama:11434resolves. - Disk fills up: Models are large. Check
docker system dfand prune carefully (docker image prune) after you confirm you don’t need old images. - Slow responses: Start with smaller models, reduce concurrent users, or plan for a GPU machine.
Security notes for a self-hosted AI endpoint
Run LibreChat like you’d run an admin panel:
- Patch regularly. Container images and the host OS both matter.
- Minimize exposed ports. Only 80/443 should be public for this stack.
- Log access. Keep reverse proxy logs and Docker logs so you can investigate problems later.
- Separate roles. Give admins their own accounts and avoid shared credentials.
If you want to go deeper on web hardening (headers, TLS settings, safer deploy habits), this HostMyCode guide is a solid reference: web server hardening on a VPS.
If you’re building a private AI chat for a team, reliability matters more than a one-time install. Start with a HostMyCode VPS for a straightforward single-host setup, or use managed VPS hosting if you want help with updates, monitoring, and baseline security while you run the AI stack.
FAQ
Do I need a GPU to self-host LibreChat with Ollama?
No. A CPU-only VPS works for small models and light usage. If you want higher-quality models and multiple concurrent users, a GPU (or a bigger dedicated server) becomes the practical path.
Should I expose Ollama’s port to the internet?
Avoid it. Keep Ollama on an internal Docker network and expose only LibreChat through HTTPS. If you must expose it, put it behind auth and strict rate limits.
How do I control who can create accounts?
Set ALLOW_REGISTRATION=false so only existing admins can add users (invite-only workflow). Also consider restricting access by IP or VPN for internal deployments.
What’s the simplest way to add RAG later?
Run a vector store next to this stack. If you prefer PostgreSQL, pgvector is a clean default on hosting infrastructure. This HostMyCode tutorial shows a production-friendly setup: set up pgvector for RAG on a PostgreSQL VPS.
Summary
You now have LibreChat serving a private chat UI over HTTPS, backed by Ollama for local model inference.
Start small, measure latency, and scale model size or concurrency only after you’ve validated performance.
If you want a stable base for this setup, deploy it on a HostMyCode VPS. Move up to a larger plan or a dedicated server once your team depends on it every day.