Workload · Running a private AI chat

LibreChat self-hosted: one chat app for your team, on a server you control

Self-hosting LibreChat means running the open-source chat app on your own server with Docker Compose: you clone the project, give it secret keys of your own, sign up as the first user (who becomes the admin) while nobody else can reach it, then switch sign-ups off and open it to your team over HTTPS. Add Ollama on a server with a graphics card and your team can chat with models running on that machine, next to any hosted provider you choose to connect.

The short version
  1. The chat app is light; a card only matters for local models
  2. Your conversations sit in MongoDB on your own disks
  3. Whoever registers first becomes admin, so make that you
  4. Free to use and change under the MIT licence

Do you need a graphics card for this?

Only if you want models running on your own machine. LibreChat itself never runs a model: it passes each message to whichever provider or engine you have connected and shows the reply. Its documentation puts the minimum at 1 GiB of memory and one vCPU, and suggests making 2 GB available once everything in the default setup is running.

The card is for the engine. A model's weights have to fit in its memory: as a rough floor from our own catalogue, a 7-8 billion parameter model compressed to 4-bit needs about 5 GB, a 30-34B one about 19 GB, a 70B one about 38 GB, and you want a quarter to a half more on top for the conversation itself. Our LLM hosting guide explains how to pick the size.

What comes in the box

  • The app (api): the web interface and the server behind it, on port 3080.
  • MongoDB: stores your accounts, conversations and settings in the project's data-node folder.
  • Meilisearch: lets people search their old chats by any word in them, with typos forgiven.
  • RAG API and a pgvector database: let people ask questions about files they upload.
  • Admin panel: defined in the stock setup, but it refuses to start (and keeps retrying) until .env contains ADMIN_PANEL_SESSION_SECRET.

None of the helpers is published to the outside world by the stock setup; they talk to each other on a private Docker network. Only the app's port is published, and the next step decides who can reach it.

Get the server ready

On a fresh Ubuntu server, install Docker and the Compose plugin from the apt repository Docker publishes, the way LibreChat's Ubuntu guide shows, and add yourself to the docker group. Then put up the firewall before any service starts. Two things are worth knowing here. Turning on IPv6 support in ufw has to happen before you add rules, or the rules only protect IPv4. And Docker adds firewall rules of its own for every port it publishes, which skip ufw entirely, so the real protection for the chat app is publishing it on 127.0.0.1 only, as shown further down.

# Firewall first: IPv6 on, then rules, then enable
sudo sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw
sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Lets the LibreChat containers reach Ollama on the host
sudo ufw allow proto tcp from 172.16.0.0/12 to any port 11434
sudo ufw --force enable

# Get LibreChat and give it its own keys
git clone https://github.com/LibreChat-AI/LibreChat.git ~/LibreChat
cd ~/LibreChat
cp .env.example .env && chmod 600 .env
sed -i -E '/^(CREDS_KEY|CREDS_IV|JWT_SECRET|JWT_REFRESH_SECRET|MEILI_MASTER_KEY|SEARCH)=/d' .env
for k in CREDS_KEY JWT_SECRET JWT_REFRESH_SECRET MEILI_MASTER_KEY; do
  printf '%s=%s\n' "$k" "$(openssl rand -hex 32)" >> .env
done
printf 'CREDS_IV=%s\n' "$(openssl rand -hex 16)" >> .env
printf 'SEARCH=true\n' >> .env
# Run the containers as you, and create their data folders as yours
printf 'UID=%s\nGID=%s\n' "$(id -u)" "$(id -g)" >> .env
mkdir -p data-node uploads images logs "$(grep -o 'meili_data_v[0-9.]*' docker-compose.yml | head -n1)"

The keys are written straight into .env by the shell's built-in printf, so they never show up in the process list or your command history; after chmod 600 only your user can read it. CREDS_KEY and CREDS_IV encrypt saved credentials in the database, the JWT secrets sign logins, MEILI_MASTER_KEY guards search. Setting UID and GID to your own numbers means the containers act as your user: they can open the locked-down .env and save into their folders. Keep a safe copy: if CREDS_KEY or CREDS_IV ever changes, older encrypted records become unreadable.

Keep it private while you set it up

Create two files in ~/LibreChat. The override file changes the stock setup without editing it, so updates never fight your changes. It publishes the app and the admin panel on 127.0.0.1 only. The !override tag matters: without it Compose adds your port line to the stock one instead of replacing it, and only newer Docker Compose releases understand !override. The override also moves you onto librechat:latest, the image that tracks finished releases instead of development builds made from main. To freeze on one version until you decide otherwise, write a specific release such as :v0.8.8 in its place. The second file tells LibreChat where Ollama is.

# ~/LibreChat/docker-compose.override.yml
services:
  api:
    image: registry.librechat.ai/librechat-ai/librechat:latest
    ports: !override
      - "127.0.0.1:3080:3080"
    volumes:
      - type: bind
        source: ./librechat.yaml
        target: /app/librechat.yaml
  admin-panel:
    ports: !override
      - "127.0.0.1:3000:3000"

# ~/LibreChat/librechat.yaml
version: 1.3.18
cache: true
endpoints:
  custom:
    - name: "Ollama"
      apiKey: "ollama"
      baseURL: "http://host.docker.internal:11434/v1/"
      models:
        default: ["llama3:latest"]
        fetch: true
      titleConvo: true
      titleModel: "current_model"

Start everything with docker compose up -d. The first start downloads the images and takes a few minutes.

Become the admin before anyone else can

There is no default login. Whoever registers first gets the admin account, so do it through a private tunnel while the app is still invisible to the internet.

  1. On your own computer, run ssh -L 3080:127.0.0.1:3080 you@your-server and leave it open.
  2. Visit http://localhost:3080 in your browser and register.
  3. Back on the server, stop strangers signing up: open .env with nano .env, change ALLOW_REGISTRATION=true to false, save, then run docker compose down followed by docker compose up -d so the app reads the new setting.
  4. Add each teammate yourself. The app's container is named LibreChat, so docker exec -it LibreChat npm run create-user asks for their email and a password; docker exec -it LibreChat npm run delete-user their@email.com removes someone.

LibreChat also watches for abuse on its own: too many login attempts, sign-ups or messages add up, and past a threshold the person and their IP address are locked out for a while. If you want every local account to use an authenticator app, set ENFORCE_TWO_FACTOR_AUTHENTICATION=true in .env.

Connecting models that run on your machine

Install Ollama on the server itself with our Ollama guide and pull a model. By default Ollama only answers on 127.0.0.1, and from inside a container that address means the container, not your server. So tell Ollama to listen more widely: run sudo systemctl edit ollama, add Environment="OLLAMA_HOST=0.0.0.0" under [Service], and restart it.

That is safe only because of the firewall. Ollama has no password of its own, so anyone who could reach port 11434 could use your models or delete them. Your ufw rules let in only Docker's private address range on that port. LibreChat's docs go one step further and suggest binding Ollama to the Docker network's gateway address alone. The apiKey line in librechat.yaml can say anything, but it must not be empty. If you also use hosted providers, add them to the same list and keep their keys in .env, written as ${NAME} in the YAML, or set user_provided so each person brings their own.

Opening it to your team over HTTPS

Point a name such as chat.yourcompany.com at the server, install nginx and certbot, and save this as /etc/nginx/sites-available/librechat:

server {
    listen 80;
    listen [::]:80;
    server_name chat.yourcompany.com;
    client_max_body_size 25M;
    location / {
        proxy_pass http://127.0.0.1:3080;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

Enable it with a link in sites-enabled, check it with sudo nginx -t, reload nginx, and run sudo certbot --nginx -d chat.yourcompany.com to get the certificate. The forwarded-address header lets LibreChat see each visitor's real IP, which its lockouts depend on, and the 25 MB upload limit matches the one in LibreChat's own nginx file. Last, open .env once more, put your https:// address on both domain lines (DOMAIN_CLIENT= and DOMAIN_SERVER=), and bring the stack down and up again.

Keeping it up to date

From ~/LibreChat: docker compose down, then git pull, docker compose pull and docker compose up -d. LibreChat's guide also deletes the previous LibreChat images to free disk space. Glance at the release notes first, since a version can reindex search or come with a migration script to run.

Take a copy before each update, with the stack stopped: the data-node, meili_data_*, uploads and images folders, .env, librechat.yaml, and the two Docker volumes the stack creates for its working data and the vector database. Treat that copy like a password, because .env holds your encryption keys. Keeping those copies, and deciding where they live, is your job: store them somewhere other than this server.

Is a GPU server the right home for it?

If your team will chat with local models, yes. With one of our GPU dedicated servers, nobody else touches the card: it is handed directly to the system you install. There's no hypervisor in between, no slice of it given to someone else, and nobody's batch job slowing your replies in the afternoon. The whole machine comes with it: root, every core, and an iLO or iDRAC console belonging to that box for when a network change locks you out. Traffic on the port isn't metered either way, there's no contract, and you choose from five cities. A machine that already has your card in it is ready around 30 minutes after we approve the order; if we fit a card we hold, allow 4 to 24 hours, and 5 to 10 working days if the card has to be bought in (10 to 15 for a scarce accelerator).

If you will only connect hosted providers, you don't need a card at all: an ordinary dedicated server runs LibreChat comfortably. And if you would rather manage everything from the browser in a single container, Open WebUI is worth a look.

Questions people ask

Is LibreChat free for a business to run?
Yes. LibreChat is released under the MIT licence, which lets you use, change and share it, commercially too. The one condition is keeping its copyright and licence notice with any copy of the code you pass on.
Why does LibreChat need MongoDB and Meilisearch?
MongoDB is where everything lives: accounts, conversations and settings. Meilisearch powers the search box over old chats, and search only works when SEARCH=true and a master key are both set. The stock Docker setup runs both for you on a private network, so there is nothing extra to install.
Can my teammates sign themselves up?
They can while ALLOW_REGISTRATION is true, but on a server the internet can reach that means anyone can. Most teams switch it off after the admin account exists and add people with the create-user script, which works even with registration closed. Sign-in through your own identity provider is another option the docs cover.
What happens to my data when I update?
The update pulls new images and recreates the containers; your data stays in the project folders and Docker volumes, which are left alone. Some releases rebuild the search index or ship a migration script, so read the notes and take a copy first.

Give your team a private AI chat

Pick a card with room for the model you want, and run LibreChat right beside it.

Find a GPU server for your chat

Read next