Skip to content

Local Testing (Docker Compose)

This guide runs both layers locally:

  • backend: FastAPI on http://localhost:8001
  • frontend: Next.js on http://localhost:3000

No VPS web server (nginx) or TLS is used locally.

Prerequisites

  1. Docker installed.
  2. Docker Compose plugin available (or docker-compose).
  3. If you want the AI endpoints to work fully, you need an Ollama server reachable from the backend container.

Step 1: Create environment files

From the repo root (/home/paul/Apps/LNA/AI/lna-ai-api):

cp backend/docker/.env.backend.example backend/docker/.env.backend
cp frontend/docker/.env.frontend.example frontend/docker/.env.frontend

Step 2: Configure backend env (backend/docker/.env.backend)

At minimum, the backend must be able to start. Set:

  • SUPABASE_URL
  • SUPABASE_KEY

Then decide how you want the model provider to work:

The code uses Ollama for:

  • chat generation (via the OpenAI-compatible streaming client)
  • embeddings (via Ollama embeddings)

If Ollama is running on your host machine, DO NOT use localhost inside the container. Use your host LAN IP:

  • Set:
  • LNA_LLM_PROVIDER=ollama
  • OLLAMA_HOST=http://<YOUR_LAN_IP>:11434
  • OLLAMA_EMBEDDING_HOST=http://<YOUR_LAN_IP>:11434

If Ollama is not reachable, the backend may still start, but /answer/stream will fail when called.

Option B: LNA_LLM_PROVIDER=deepseek

If you use DeepSeek, set:

  • LNA_LLM_PROVIDER=deepseek
  • DEEPSEEK_API_KEY
  • DEEPSEEK_BASE_URL (optional, defaults to https://api.deepseek.com)
  • DEEPSEEK_MODEL (optional, defaults to deepseek-chat)

Note: embeddings still use Ollama. You still need Ollama running somewhere reachable and set OLLAMA_EMBEDDING_HOST.

Step 3: Configure frontend env (frontend/docker/.env.frontend)

Set for local development:

  • NEXT_PUBLIC_APP_URL=http://localhost:3000
  • NEXT_PUBLIC_API_URL=http://localhost:8001
  • NEXT_PUBLIC_AI_API_URL=http://localhost:8001

Step 4: Start everything

From the repo root:

docker compose up --build

First build can take a few minutes.

Understanding docker-compose.yml (what gets built/run)

This repo’s docker-compose.yml defines two services: backend and frontend.

  • Paths are relative: paths like env_file: backend/docker/.env.backend and dockerfile: backend/docker/Dockerfile are resolved relative to the folder that contains docker-compose.yml (the repo root).

backend service

  • Build:
  • context: . means the Docker build context is the repo root (so the Dockerfile can copy both backend/ and lna_ai_agent.py from the root).
  • dockerfile: backend/docker/Dockerfile selects the backend Dockerfile.
  • Environment:
  • env_file: backend/docker/.env.backend loads variables into the container (Supabase/LLM config, etc).
  • Ports:
  • 8001:8001 exposes the API on your machine at http://localhost:8001.
  • Restart policy:
  • unless-stopped restarts the container automatically if it crashes.

frontend service

  • Build:
  • context: . uses the repo root as build context.
  • dockerfile: frontend/docker/Dockerfile selects the frontend Dockerfile.
  • Environment:
  • env_file: frontend/docker/.env.frontend loads public Next.js env vars.
  • Dependency:
  • depends_on: [backend] starts backend before frontend (it does not wait for backend to be β€œready”, only started).
  • Ports:
  • 3000:3000 exposes the app on http://localhost:3000.

What docker compose up --build does

  • docker compose: reads ./docker-compose.yml in the current directory.
  • up: creates a private network for the project, then creates & starts the containers defined in the compose file.
  • --build: if an image is missing (or the Dockerfile/context changed), it builds the images first, then starts containers from those images.
  • Ports: because docker-compose.yml maps ports, you can reach services on your host:
  • backend: localhost:8001
  • frontend: localhost:3000

Useful compose commands (local testing)

  • Start (build if needed):
docker compose up --build
  • Start in background:
docker compose up -d --build
  • Stop containers (keeps images):
docker compose down
  • Rebuild everything from scratch (no cache):
docker compose build --no-cache
docker compose up

Step 4A: Build images individually (without starting containers)

This is useful when you want to confirm Docker builds before running.

From the repo root:

  • Build only backend image:
docker compose build backend
  • Build only frontend image:
docker compose build frontend
  • Build both images:
docker compose build

Step 4B: Start containers individually (after building)

From the repo root:

  • Start only backend:
docker compose up backend
  • Start only frontend (will also start backend because it is a dependency):
docker compose up frontend

Tip: add -d to run detached:

docker compose up -d backend

Step 4C: Build Dockerfiles directly (without compose)

This is the β€œraw Docker” way. It’s helpful to understand what compose is doing.

From the repo root:

  • Build backend image (uses backend/docker/Dockerfile):
docker build -t lna-ai-backend:local -f backend/docker/Dockerfile .
  • Build frontend image (uses frontend/docker/Dockerfile):
docker build -t lna-ai-frontend:local -f frontend/docker/Dockerfile .

If you build this way, you must also docker run ... with ports and env yourself (compose normally handles that wiring for you).

Step 5: Verify backend

Open a new terminal and run:

curl http://localhost:8001/health

You should receive a JSON response with status: "healthy".

Step 6: Verify frontend

Open in your browser:

  • http://localhost:3000

Step 7: View logs / troubleshoot

Backend logs:

docker compose logs -f backend

Frontend logs:

docker compose logs -f frontend

Common failures:

  • Backend container exits immediately:
  • Check SUPABASE_URL / SUPABASE_KEY are set correctly.
  • /answer/stream fails:
  • Check OLLAMA_HOST / OLLAMA_EMBEDDING_HOST are reachable from inside the container.

Next step (optional)

If you want, I can modify docker-compose.yml to also run an Ollama container on the same Docker network so you don’t need LAN IPs.