---
title: Deploying the Docs Site
description: Production deployment of nibiru-framework.com using jwilder/nginx-proxy and your own Ollama on neuronetz.ai.
---

This page documents how the docs site is deployed in production. The setup uses **jwilder/nginx-proxy** for automatic Docker container routing, **letsencrypt-nginx-proxy-companion** for HTTPS, and **your own Ollama at api.neuronetz.ai** for the Oracle backend — no paid LLM API keys required.

## Topology

```
                     Internet
                        │
                        ▼
            ┌──────────────────────┐
            │ jwilder/nginx-proxy  │  ← reverse proxy on :80 / :443
            │ (network: nginx-proxy)│    terminates TLS
            └──────────┬───────────┘
                       │ http://nibiru-docs:4321
                       ▼
            ┌──────────────────────┐         ┌──────────────────────┐
            │ nibiru-docs          │ ──────▶ │ api.neuronetz.ai     │
            │ Astro Node SSR :4321 │ HTTPS   │ Ollama (5× GPU)      │
            │ Oracle endpoint      │         │ qwen2.5-coder:14b    │
            └──────────────────────┘         │ nomic-embed-text     │
                                             └──────────────────────┘
```

## Prerequisites on the host

```bash
# 1) Create the shared external network (one time)
docker network create nginx-proxy

# 2) Run nginx-proxy + acme-companion (one time)
#    See https://github.com/nginx-proxy/nginx-proxy for the canonical compose.

# 3) Pull the Oracle's models on neuronetz.ai (one time)
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
```

## Files in `docs/`

| File | Purpose |
|---|---|
| `Dockerfile` | Multi-stage build: builds Oracle index against neuronetz.ai, builds Astro, prunes dev deps. |
| `docker-compose.yml` | Production — `VIRTUAL_HOST=nibiru-framework.com`, joined to `nginx-proxy` network. |
| `docker-compose.local.yml` | Local-test override — exposes `4321:4321`, drops nginx-proxy env vars. |
| `.dockerignore` | Keeps `node_modules`, `.git`, etc. out of the build context. |
| `.env.example` | Template — defaults to Ollama on neuronetz.ai, no API keys required. |

## Bring it up

```bash
cd docs
cp .env.example .env
# defaults are fine for production unless you want to override the Ollama URL or models

docker compose up -d --build
```

The first build runs `build-oracle-index.mjs` to embed the docs against your Ollama. Subsequent rebuilds are fast — Docker caches the dep layer; only changed chunks need re-embedding.

After ~30 seconds, jwilder/nginx-proxy picks up the new container, requests a Let's Encrypt cert, and routes `https://nibiru-framework.com` → `:4321`.

## Verify

```bash
curl -s https://nibiru-framework.com/api/oracle | jq
# {
#   "status": "ok",
#   "llm":   { "provider": "ollama", "model": "qwen2.5-coder:14b", … },
#   "embed": { "provider": "ollama", "model": "nomic-embed-text", … },
#   "index": { "present": true, "chunks": 177, … }
# }
```

Or open the site in a browser, click the amber Oracle launcher, and ask a question.

## Update after a docs change

```bash
git pull
docker compose up -d --build
```

The build re-runs the Oracle index against the freshest content; the new container starts on `:4321`; jwilder swaps the upstream; the old container is stopped — no noticeable downtime.

## Environment variables

| Var | Default | Used at | Purpose |
|---|---|---|---|
| `LLM_PROVIDER` | `ollama` | runtime | `ollama` (default) or `anthropic`. |
| `OLLAMA_BASE_URL` | `https://api.neuronetz.ai` | build + runtime | Where to reach Ollama. |
| `OLLAMA_CHAT_MODEL` | `qwen2.5-coder:14b` | runtime | Chat-completion model. |
| `OLLAMA_EMBED_MODEL` | `nomic-embed-text` | build + runtime | Embedding model. |
| `EMBED_PROVIDER` | `ollama` | build + runtime | `ollama` or `openai`. |
| `ANTHROPIC_API_KEY` | — | runtime | Only used if `LLM_PROVIDER=anthropic`. |
| `ANTHROPIC_MODEL` | `claude-haiku-4-5-20251001` | runtime | Override Claude model. |
| `OPENAI_API_KEY` | — | build + runtime | Only used if `EMBED_PROVIDER=openai`. |
| `ORACLE_TOP_K` | `6` | runtime | Chunks injected per Oracle response. |
| `LETSENCRYPT_EMAIL` | `stephan.kasdorf@bittomine.com` | letsencrypt | Where Let's Encrypt sends expiry notices. |
| `VIRTUAL_HOST` | `nibiru-framework.com,www.nibiru-framework.com` | nginx-proxy | Set in compose. |

## Troubleshooting

**`502 Bad Gateway`.** The upstream container failed to start. Check
`docker logs nibiru-docs` — likely missing build artifact in `dist/server/entry.mjs`.

**Cert not issued.** Let's Encrypt rate-limits aggressively. Check
`docker logs letsencrypt-nginx-proxy-companion` for the cause.

**Oracle answers without citations.** The embedding index is empty. Either
`nomic-embed-text` isn't pulled on Ollama, or the build couldn't reach
neuronetz.ai. Re-pull the model:
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
docker compose up -d --build
```

**Oracle returns "the Oracle could not answer".** Check the chat model is pulled:
```bash
curl https://api.neuronetz.ai/api/tags | jq '.models[].name'
```

**Want to fall back to Claude.** Set `LLM_PROVIDER=anthropic` and `ANTHROPIC_API_KEY` in `.env`, then `docker compose up -d`.

## Resource usage

| | Idle | Under Oracle load |
|---|---|---|
| RAM | ~120 MB | ~200 MB |
| CPU | < 0.5% | ~5% |
| Network | minimal | one HTTPS round-trip per question |
| Disk | ~60 MB image + ~5 MB index | + corpus exports |

A 1 GB / 1 vCPU droplet handles the docs site comfortably alongside other services. The heavy lifting (LLM inference) happens on your GPU cluster, not the docs container.
