# How to install AnythingLLM with Docker, HTTPS and Ollama

> Self-host AnythingLLM with Docker on Ubuntu or Debian: persistent storage, multi-user login, HTTPS through Caddy, a local Ollama connection and backups.

Difficulty: Intermediate\
Tested on: Ubuntu 24.04 LTS, Ubuntu 26.04 LTS, Debian 12, Debian 13

AnythingLLM is an all-in-one AI application: you collect documents in workspaces, chat with them through the language model of your choice, and run agents that can browse the web or call tools. The Docker edition is the multi-user version meant for servers, with roles, password protection and an API.

This guide runs the official `mintplexlabs/anythingllm` image with Docker Compose on Ubuntu or Debian. You keep all data in a host folder, set your own signing secrets, switch on multi-user mode through an SSH tunnel before the app is reachable from the internet, publish it over HTTPS with Caddy and connect it to an Ollama server on the same machine. The guide ends with backups, updates and fixes for the errors people hit most often.

## Prerequisites

- A server running **Ubuntu 24.04 LTS**, **Ubuntu 26.04 LTS**, **Debian 12** or **Debian 13** with Docker Engine and the Compose plugin. Follow [Install Docker on Ubuntu](/guides/install-docker-ubuntu) or [Install Docker on Debian](/guides/install-docker-debian) first.
- A non-root user with `sudo` rights and SSH key login, see [Secure a new Linux server](/guides/secure-a-new-linux-server) and [Set up SSH keys](/guides/ssh-keys).
- A domain name such as `anythingllm.example.com` with an A (and optionally AAAA) record pointing at the server, and Caddy installed as described in [Caddy reverse proxy](/guides/caddy-reverse-proxy).
- A language model: an API key from a hosted provider, or Ollama installed with [Install Ollama](/guides/install-ollama).

AnythingLLM's documentation publishes these minimums for the Docker version:

| Resource | Minimum (official) | Suggested starting point |
|---|---|---|
| CPU | 2 cores, with AVX2 on x86 | 2–4 vCPU |
| RAM | 2 GB | 4 GB, plus the memory your local models need |
| Disk | 5 GB | 20 GB or more; it grows with your documents |

The suggested column is a conservative starting point, not a benchmark. If Ollama runs on the same server, add the model's own memory requirement on top.

On x86 servers the CPU **must** support AVX2, because the default LanceDB vector database crashes without it. ARM64 servers use a separate build and are not affected. Check before you start:

```bash
lscpu | grep -o avx2
```

If the command prints `avx2`, you are ready. If it prints nothing, pick a server whose CPU exposes AVX2.

## Step 1 — Create the project folder and secrets

Keep everything for this app under `/opt/anythingllm`. The `storage` folder becomes the container's data directory. AnythingLLM also writes its own settings to a `.env` file inside that folder, which must exist as a file before the first start; otherwise Docker would create a directory in its place.

```bash
sudo mkdir -p /opt/anythingllm/storage
sudo chown -R $USER:$USER /opt/anythingllm
cd /opt/anythingllm
touch storage/.env
```

Next, create a second `.env` file next to the Compose file. Compose reads it and passes three secrets to the container: `JWT_SECRET` signs login sessions, while `SIG_KEY` and `SIG_SALT` are the signing values the example configuration asks you to set to random strings of at least 32 characters.

```bash
cat > .env <<EOF
JWT_SECRET=$(openssl rand -hex 32)
SIG_KEY=$(openssl rand -hex 32)
SIG_SALT=$(openssl rand -hex 32)
EOF
chmod 600 .env
sudo chown -R 1000:1000 storage
```

The container runs as the user with UID and GID 1000, so it must own `storage`. Do not use `chmod -R 777` as a shortcut: the folder holds your database, documents and any API keys you enter in the app.

## Step 2 — Write the Compose file

Create `/opt/anythingllm/compose.yaml`:

```yaml
services:
  anythingllm:
    image: mintplexlabs/anythingllm:latest
    container_name: anythingllm
    restart: unless-stopped
    ports:
      - "127.0.0.1:3001:3001"
    cap_add:
      - SYS_ADMIN
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      - STORAGE_DIR=/app/server/storage
      - JWT_SECRET=${JWT_SECRET}
      - SIG_KEY=${SIG_KEY}
      - SIG_SALT=${SIG_SALT}
      - DISABLE_SWAGGER_DOCS=true
    volumes:
      - ./storage:/app/server/storage
      - ./storage/.env:/app/server/.env
```

What each part does:

- `127.0.0.1:3001:3001` publishes the web interface on the loopback address only. Docker bypasses ufw for published ports, so binding to localhost is what keeps port 3001 off the internet; Caddy will be the only public entry point.
- `cap_add: SYS_ADMIN` follows the official run command. The documentation calls it required for scraping web pages, because the scraper runs Chromium in a sandbox. If you will never add websites as documents, you can delete these two lines.
- `extra_hosts` maps `host.docker.internal` to the host, so the container can reach Ollama on the same server in Step 6.
- `DISABLE_SWAGGER_DOCS=true` turns off the `/api/docs` page, which the example configuration recommends for production.

> **Tip**
>
> The official instructions use the `latest` tag. To control when you upgrade, replace `latest` with a release tag from the [AnythingLLM releases page](https://github.com/Mintplex-Labs/anything-llm/releases), for example `1.17.0`, and change it deliberately when you update.

## Step 3 — Start AnythingLLM and check it

```bash
docker compose up -d
docker compose ps
docker compose logs --tail 50 anythingllm
```

`docker compose ps` should show the `anythingllm` container as running with `127.0.0.1:3001->3001/tcp`. Then ask the app for its start page from the server itself:

```bash
curl -I http://127.0.0.1:3001
```

You should see `HTTP/1.1 200 OK`. If the container restarts in a loop, read the logs and check the troubleshooting section below.

## Step 4 — Finish setup through an SSH tunnel

Right after the first start, anyone who can open AnythingLLM can configure it. Do the first-run setup over an SSH tunnel, so the app is never exposed without a password. On your own computer, run:

```bash
ssh -L 3001:127.0.0.1:3001 youruser@203.0.113.10
```

Keep that session open and browse to `http://localhost:3001`. The onboarding screens ask for your LLM provider, the embedding model (the built-in embedder is fine to start) and the vector database (keep LanceDB). You can change all of these later.

Then open the settings and turn on **Enable multi-user mode**. Enter a username and a strong password for the first admin account; AnythingLLM logs you out, and you sign in again with the new account. Three roles are available:

- **Admin** has full access to the whole system.
- **Manager** sees all workspaces and most settings, but cannot change the LLM, embedder or vector database.
- **Default** can only chat in the workspaces an admin or manager adds them to.

> **Warning**
>
> Multi-user mode cannot be switched off again. If only you will ever use the instance, the single-user alternative is **Password Protect Instance**, but anyone with that password can change every setting and read every chat.

## Step 5 — Publish AnythingLLM over HTTPS with Caddy

Add a site block for your domain to `/etc/caddy/Caddyfile`:

```caddyfile
anythingllm.example.com {
    reverse_proxy 127.0.0.1:3001
}
```

Reload Caddy and test the public address:

```bash
sudo systemctl reload caddy
curl -I https://anythingllm.example.com
```

Caddy obtains the certificate on the first request and returns `HTTP/2 200`. It proxies WebSocket connections automatically and applies no read timeout by default, which suits AnythingLLM's agent sessions and long answers. If you prefer Nginx, follow [Nginx with Certbot](/guides/nginx-reverse-proxy-certbot) and forward the `Upgrade` and `Connection` headers, as the AnythingLLM cloud guide shows; [Traefik](/guides/traefik-reverse-proxy) works too.

Make sure the firewall only allows SSH and web traffic:

```bash
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
```

## Step 6 — Connect Ollama on the same server

If you followed [Install Ollama](/guides/install-ollama), Ollama listens on `127.0.0.1:11434`, which a container cannot reach. AnythingLLM's documentation explains that `localhost` inside the container means the container itself. Make Ollama listen on all addresses with a systemd drop-in, as the Ollama FAQ describes:

```bash
sudo mkdir -p /etc/systemd/system/ollama.service.d
sudo tee /etc/systemd/system/ollama.service.d/override.conf <<'EOF'
[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"
EOF
sudo systemctl daemon-reload
sudo systemctl restart ollama
ss -tln | grep 11434
```

The last command should show Ollama on `0.0.0.0:11434` or `*:11434`. If `override.conf` already exists, add the `Environment` line to it instead of replacing the file.

> **Warning**
>
> Ollama has no authentication. Unlike Docker's published ports, a service running directly on the host is protected by ufw, so keep ufw enabled and never add a rule that opens port 11434 to everyone.

ufw also blocks traffic from Docker networks to the host by default. Find the subnet of the Compose network and allow only that subnet to reach Ollama:

```bash
docker network inspect anythingllm_default | grep Subnet
sudo ufw allow from 172.18.0.0/16 to any port 11434 proto tcp
sudo ufw status
```

Replace `172.18.0.0/16` with the subnet the first command printed. This rule only protects Ollama while ufw is active with its default deny policy, so confirm that `sudo ufw status verbose` shows `Status: active` and `deny (incoming)`, and that `nc -vz your-server-ip 11434` from another machine fails. Now open AnythingLLM's LLM settings, choose **Ollama** and set the base URL to `http://host.docker.internal:11434`. The model list fills with the models you pulled with `ollama pull`; pick one and save. You can also switch the embedder to Ollama (for example with an embedding model such as `nomic-embed-text`), but the built-in embedder works without extra setup. For larger models, consider a [GPU server](/gpu-servers).

## Back up and restore

All state lives in `/opt/anythingllm`: the `storage` folder (SQLite database, documents, vector data, app settings) plus `compose.yaml` and the secrets in `.env`. Stop the container briefly so the database and vector files are consistent, then archive the folder:

```bash
sudo mkdir -p /opt/backups
cd /opt/anythingllm
docker compose stop
sudo tar czf /opt/backups/anythingllm-$(date +%F).tar.gz -C /opt anythingllm
docker compose start
```

To restore on the same or a new server with Docker installed, unpack the archive and start the stack:

```bash
sudo tar xzf /opt/backups/anythingllm-2026-10-09.tar.gz -C /opt
cd /opt/anythingllm
docker compose up -d
```

Restore with the same image version or a newer one, never an older one. The archive contains your secrets and every document, so encrypt it if it leaves the server, and copy backups off the server, for example to object storage or another machine.

## Update AnythingLLM

Read the [release notes](https://github.com/Mintplex-Labs/anything-llm/releases) first and take a backup. Then pull the new image and recreate the container; the data in `storage` is kept:

```bash
cd /opt/anythingllm
docker compose pull
docker compose up -d
docker image prune -f
```

If you pinned a version tag, change it in `compose.yaml` before `docker compose pull`. Check `docker compose logs --tail 50 anythingllm` afterwards; database migrations run during startup.

## Troubleshooting

### The container exits with Illegal instruction

The CPU does not expose AVX2, which the default LanceDB vector database needs on x86. Confirm with `lscpu | grep -o avx2`. Move to a server whose CPU exposes AVX2, or set a different vector database in AnythingLLM's configuration; those run as separate services you would have to host.

### unable to open database file

The container cannot write to the storage folder, usually because its owner is not UID 1000. Fix the ownership and restart:

```bash
sudo chown -R 1000:1000 /opt/anythingllm/storage
docker compose restart
```

### ECONNREFUSED to port 11434 or an empty Ollama model list

Ollama still listens on `127.0.0.1`, or ufw blocks the Docker subnet. Check `ss -tln | grep 11434` on the host, compare the subnet from `docker network inspect anythingllm_default | grep Subnet` with `sudo ufw status`, and make sure the base URL is `http://host.docker.internal:11434`, not `localhost`.

### Website scraping fails with No usable sandbox

The scraper's Chromium needs the `SYS_ADMIN` capability. Add the `cap_add` lines from Step 2 back to `compose.yaml` and run `docker compose up -d`.

### Caddy returns 502 Bad Gateway

The container is stopped or listens on another port. Run `docker compose ps` and `curl -I http://127.0.0.1:3001`; the site block must point at the same port that `compose.yaml` publishes.

## Next steps

- Run your own models with [Install Ollama](/guides/install-ollama), or add a chat front end with [Open WebUI and Ollama](/guides/open-webui-ollama).
- Learn more about the Compose file format in [Docker Compose basics](/guides/docker-compose-basics).
- Read the official [AnythingLLM documentation](https://docs.anythingllm.com/) for agents, the developer API and embeddable chat widgets.
- See server options for this app on the [AnythingLLM hosting](/anythingllm-hosting) page.

## Frequently asked questions

### Does AnythingLLM need a GPU?

No. AnythingLLM itself runs on the CPU and the language model does the heavy work. Use a hosted provider such as OpenAI or Anthropic, or run Ollama on the same or another server. Larger local models answer much faster on a server with a supported NVIDIA GPU.

### Why does the container need the SYS\_ADMIN capability?

AnythingLLM's documentation lists --cap-add SYS_ADMIN as required when you want to scrape web pages, because the scraper runs a sandboxed Chromium. If you never add websites as documents you can leave the capability out; the rest of the app works without it.

### Can I switch back to single-user mode after enabling multi-user mode?

No. The AnythingLLM documentation states that multi-user mode cannot be turned off once it is enabled. Plan your admin, manager and default users before you invite people.

### Why does the AnythingLLM container exit with Illegal instruction?

On x86 servers the default LanceDB vector database needs a CPU with the AVX2 instruction set. Run lscpu | grep -o avx2 on the server; if it prints nothing, choose a server whose CPU exposes AVX2 or configure a different vector database.

### Where does AnythingLLM keep documents, chats and settings?

Everything lives in the storage folder mounted at /app/server/storage: the SQLite database, uploaded and processed documents, vector data and the app-managed .env file. Back up that folder and you can rebuild the whole instance.

---

Source: <https://hyperdc.com/guides/tutorials/install-anythingllm>\
Updated: 2026-10-09
