# Installation

> Prerequisites, install script, manual Docker Compose setup, required secrets, and optional stack scaling (external Postgres, S3, no ai-agent).

- Repository: Paca-AI/paca
- GitHub: https://github.com/Paca-AI/paca
- Human docs: https://www.grok-wiki.com/public/docs/paca-ai-paca-f238b2ab3d25
- Complete Markdown: https://www.grok-wiki.com/public/docs/paca-ai-paca-f238b2ab3d25/llms-full.txt

## Source Files

- `README.md`
- `docs/guides/getting-started.md`
- `scripts/install.sh`
- `deploy/docker-compose.prod.yml`
- `deploy/.env.production.example`
- `deploy/nginx/gateway.conf`

---

---
title: "Installation"
description: "Prerequisites, install script, manual Docker Compose setup, required secrets, and optional stack scaling (external Postgres, S3, no ai-agent)."
---

Paca production installs run as a Docker Compose stack published on each GitHub release: `install.sh`, `docker-compose.yml` (from `deploy/docker-compose.prod.yml`), and `nginx/gateway.conf`. No repository clone is required. The nginx `gateway` container is the single public entrypoint on `GATEWAY_PORT` (default `80`), routing `/api/` to the Go API, `/ws/` to Socket.IO realtime, `/storage/` to MinIO, and `/` to the React SPA.

## Prerequisites

| Requirement | Details |
|---|---|
| Docker | Engine installed and daemon running (`docker info` succeeds) |
| Docker Compose | `docker compose` plugin or standalone `docker-compose` |
| Network tools | `curl` or `wget` for downloading release assets |
| Host OS | Linux server for the recommended install script path |
| Optional: Docker socket | Required only when the `ai-agent` service is enabled |

<Warning>
The `ai-agent` service mounts `/var/run/docker.sock` to spawn isolated OpenHands sandbox containers. Skip it with `--scale ai-agent=0` if the host cannot or should not expose the Docker socket.
</Warning>

## Production stack topology

```mermaid
flowchart TB
    subgraph gateway_layer["gateway (nginx)"]
        GW["gateway :80"]
    end

    subgraph app_layer["Application services"]
        WEB["web (React SPA)"]
        API["api (Go REST)"]
        RT["realtime (Socket.IO)"]
        AG["ai-agent (optional)"]
    end

    subgraph data_layer["Data services"]
        PG["postgres"]
        VK["valkey"]
        MN["minio (optional)"]
    end

    Client --> GW
    GW -->|"/"| WEB
    GW -->|"/api/"| API
    GW -->|"/ws/"| RT
    GW -->|"/storage/"| MN
    API --> PG
    API --> VK
    API --> MN
    RT --> VK
    RT --> API
    AG --> PG
    AG --> VK
    AG --> API
```

| Service | Image default | Suppress with | Notes |
|---|---|---|---|
| `postgres` | `postgres:16-alpine` | `--scale postgres=0` | Set `DATABASE_URL` for external DB |
| `valkey` | `valkey/valkey:8-alpine` | — | Cache and pub/sub; always bundled |
| `minio` | `minio/minio:latest` | `--scale minio=0` | Set `STORAGE_PROVIDER=s3` for AWS |
| `api` | `pacaai/paca-api:latest` | — | DB migrations run on startup |
| `web` | `pacaai/paca-web:latest` | `--scale web=0` | External CDN-hosted SPA |
| `realtime` | `pacaai/paca-realtime:latest` | — | Socket.IO at `/ws/socket.io` |
| `gateway` | `nginx:1.27-alpine` | — | Mounts `./nginx/gateway.conf` |
| `ai-agent` | `pacaai/paca-ai-agent:latest` | `--scale ai-agent=0` | Requires Docker socket |

## Option 1 — Install script (recommended)

The release `install.sh` downloads compose assets, prompts for configuration, generates a `.env` with strong random secrets, and starts the stack.

<Tabs>
<Tab title="Interactive">

```bash
curl -fsSL https://github.com/Paca-AI/paca/releases/latest/download/install.sh -o install.sh
bash install.sh
```

</Tab>
<Tab title="One-liner (defaults)">

```bash
curl -fsSL https://github.com/Paca-AI/paca/releases/latest/download/install.sh | bash
```

Uses defaults and auto-generated secrets. No prompts when stdin is a pipe.

</Tab>
</Tabs>

### Install script environment overrides

<ParamField body="PACA_DIR" type="string" default="./paca">
Installation directory. Created if missing; `nginx/` subdirectory is created inside it.
</ParamField>

<ParamField body="PACA_VERSION" type="string" default="latest">
Release tag to install. `latest` resolves to the newest GitHub release; a tag like `v1.2.3` pins images to `1.2.3`.
</ParamField>

<ParamField body="PACA_YES" type="string" default="0">
Set to `1` to skip all prompts and accept defaults.
</ParamField>

### Configuration prompts

The script walks through these decisions and writes the corresponding `.env` values and `--scale` flags:

| Prompt | Default choice | Effect |
|---|---|---|
| Installation directory | `./paca` | Working directory for compose files |
| Admin username | `admin` | `ADMIN_USERNAME` |
| Admin password | auto-generated 16-char alphanumeric | `ADMIN_PASSWORD` |
| Encryption key | auto-generated 64-char hex | `ENCRYPTION_KEY` for plugin secrets at rest |
| Database | Bundled PostgreSQL | External option sets `DATABASE_URL`, adds `--scale postgres=0` |
| Object storage | Self-hosted MinIO | AWS S3 option sets `STORAGE_PROVIDER=s3`, adds `--scale minio=0` |
| Gateway port | `80` | `GATEWAY_PORT`; derives default `PUBLIC_URL` |
| Public URL | derived from port | `PUBLIC_URL`; sets `COOKIE_SECURE` from scheme |
| Web app | Bundled container | External CDN option adds `--scale web=0` |
| AI agent | Enabled | Disabled option adds `--scale ai-agent=0` |

<Note>
When connecting to an existing database, supply the original `ENCRYPTION_KEY`. A different key makes previously encrypted plugin secrets permanently unreadable.
</Note>

<Steps>
<Step title="Run the install script">

```bash
curl -fsSL https://github.com/Paca-AI/paca/releases/latest/download/install.sh | bash
```

Or download first for inspection: `curl -fsSL ... -o install.sh && bash install.sh`.

</Step>

<Step title="Confirm the summary and start">

The script shows directory, version, public URL, database, storage, web, and AI agent choices. Confirm to pull images and start:

```bash
docker compose --env-file .env up -d [--scale flags] --pull always
```

</Step>

<Step title="Verify the stack">

```bash
docker compose --env-file .env ps
docker compose --env-file .env logs -f
```

Services may take up to a minute to pass health checks. Open `PUBLIC_URL` and log in with the admin credentials shown at the end (auto-generated passwords are also stored in `.env`).

</Step>
</Steps>

## Option 2 — Manual Docker Compose

Manual setup downloads the same release artifacts the install script uses, without interactive prompts.

<Steps>
<Step title="Download compose and gateway config">

```bash
mkdir paca && cd paca
curl -fsSL https://github.com/Paca-AI/paca/releases/latest/download/docker-compose.yml -o docker-compose.yml
mkdir -p nginx
curl -fsSL https://github.com/Paca-AI/paca/releases/latest/download/gateway.conf -o nginx/gateway.conf
```

</Step>

<Step title="Create `.env` with required secrets">

<CodeGroup>
```bash title="Generate secrets"
openssl rand -hex 32   # JWT_SECRET, AGENT_API_KEY, INTERNAL_API_KEY
openssl rand -hex 32   # ENCRYPTION_KEY (64-char hex)
```

```bash title="Minimal .env"
JWT_SECRET=<openssl rand -hex 32>
ADMIN_PASSWORD=<your-admin-password>
POSTGRES_PASSWORD=<openssl rand -hex 32>
AGENT_API_KEY=<openssl rand -hex 32>
INTERNAL_API_KEY=<openssl rand -hex 32>
ENCRYPTION_KEY=<openssl rand -hex 32>
PUBLIC_URL=http://localhost
```
</CodeGroup>

</Step>

<Step title="Start the full stack">

```bash
docker compose --env-file .env up -d
```

Open `PUBLIC_URL` (default `http://localhost`) and log in as `admin` with `ADMIN_PASSWORD`.

</Step>
</Steps>

## Required secrets and configuration

These variables are enforced or required for a working production deployment.

### Always required

| Variable | Generation | Constraint |
|---|---|---|
| `JWT_SECRET` | `openssl rand -hex 32` | Compose fails without it (`:?` guard in API service) |
| `ADMIN_PASSWORD` | strong password | Compose fails without it; seeds default admin on first startup |
| `PUBLIC_URL` | your hostname | Full URL, no trailing slash; used for cookies, presigned URLs, plugin callbacks |

### Required for bundled PostgreSQL

| Variable | Default | Notes |
|---|---|---|
| `POSTGRES_DB` | `paca` | Database name |
| `POSTGRES_USER` | `paca` | Database user |
| `POSTGRES_PASSWORD` | — | Override the `changeme` compose default |

### Required for plugin secret encryption

| Variable | Generation | Constraint |
|---|---|---|
| `ENCRYPTION_KEY` | `openssl rand -hex 32` | Exactly 64 lowercase hex characters (32 bytes) |

### Required when AI agent is enabled

| Variable | Generation | Notes |
|---|---|---|
| `AGENT_API_KEY` | `openssl rand -hex 32` | Must match `PACA_API_KEY` in `ai-agent` service |
| `INTERNAL_API_KEY` | `openssl rand -hex 32` | Authenticates agent service requests; empty disables auth |

<Tip>
The install script auto-generates `AGENT_API_KEY` and `INTERNAL_API_KEY` even when the AI agent is disabled. Omit them only when running with `--scale ai-agent=0`.
</Tip>

### Cookie and JWT defaults

| Variable | Default | Notes |
|---|---|---|
| `JWT_ACCESS_TTL` | `15m` | Access token lifetime |
| `JWT_REFRESH_TTL` | `168h` | Refresh token lifetime |
| `JWT_REFRESH_SESSION_TTL` | `24h` | Session-bound refresh TTL |
| `COOKIE_SECURE` | `true` in compose; `false` for `http://` URLs | Install script sets from `PUBLIC_URL` scheme |

## Optional stack scaling

Scale flags suppress bundled containers. Connection settings in `.env` must point to external equivalents.

### External PostgreSQL

```bash
# In .env:
DATABASE_URL=postgres://user:pass@host:5432/dbname

docker compose --env-file .env up -d --scale postgres=0
```

The API defaults `DATABASE_URL` to the bundled `postgres` service when unset.

### AWS S3 instead of MinIO

```bash
# In .env:
STORAGE_PROVIDER=s3
STORAGE_ENDPOINT=          # empty for default AWS regional endpoint
STORAGE_PUBLIC_URL=       # empty; S3 presigned URLs are self-contained
STORAGE_REGION=us-east-1
STORAGE_BUCKET=your-bucket
STORAGE_ACCESS_KEY_ID=your-key
STORAGE_SECRET_ACCESS_KEY=your-secret
STORAGE_USE_SSL=true

docker compose --env-file .env up -d --scale minio=0
```

For bundled MinIO, set `STORAGE_PUBLIC_URL` to `${PUBLIC_URL}/storage` so presigned URLs route through the gateway.

### Without AI agent

```bash
docker compose --env-file .env up -d --scale ai-agent=0
```

Core project management, MCP, and realtime features remain available. Autonomous agent task execution is disabled.

### External web app (CDN)

```bash
docker compose --env-file .env up -d --scale web=0
```

The gateway still serves `/api/`, `/ws/`, `/storage/`, `/plugins/`, and `/plugins-mcp/`. Build the SPA from source and deploy `dist/` to your CDN; point the CDN API proxy at `${PUBLIC_URL}/api`.

### Combined scaling

```bash
docker compose --env-file .env up -d \
  --scale postgres=0 \
  --scale minio=0 \
  --scale ai-agent=0
```

## Pinning a release version

Set image variables in `.env` to lock to a specific release tag:

```bash
PACA_API_IMAGE=pacaai/paca-api:1.2.3
PACA_WEB_IMAGE=pacaai/paca-web:1.2.3
PACA_REALTIME_IMAGE=pacaai/paca-realtime:1.2.3
PACA_AI_AGENT_IMAGE=pacaai/paca-ai-agent:1.2.3
```

Or pass `PACA_VERSION=v1.2.3` to the install script before running.

## Verification signals

| Check | Command or URL | Expected result |
|---|---|---|
| Container health | `docker compose --env-file .env ps` | `api`, `gateway`, `realtime` healthy after ~1 minute |
| API liveness | `GET ${PUBLIC_URL}/api/healthz` | `{"status":"ok"}` (no response envelope) |
| Web UI | Open `PUBLIC_URL` | Login page loads |
| Admin login | `admin` + `ADMIN_PASSWORD` | Authenticated session |
| Logs | `docker compose --env-file .env logs -f api` | No `JWT_SECRET is required` or DB connection errors |

<Check>
Database schema migrations are embedded in the API binary and run automatically on every API startup using idempotent SQL. No manual migration step is required after install.
</Check>

## Install artifacts layout

:::files
paca/
├── docker-compose.yml      # release copy of deploy/docker-compose.prod.yml
├── .env                    # generated secrets and configuration
└── nginx/
    └── gateway.conf        # nginx routes for /api, /ws, /storage, /plugins, SPA
:::

## Reconfiguration

After editing `.env`, restart the stack:

```bash
docker compose --env-file .env up -d
```

To regenerate `.env` from scratch, delete it and re-run `install.sh`. Existing `.env` files are preserved by default; the script offers to back up and replace.

## Related pages

<CardGroup>
<Card title="Quickstart" href="/quickstart">
Log in as admin, create a project, generate an API key, and verify health endpoints after install.
</Card>
<Card title="Deploy production" href="/deploy-production">
Production topology detail, secret generation reference, and operator scaling patterns.
</Card>
<Card title="Configuration reference" href="/configuration-reference">
Full environment variable catalog across API, web, realtime, and ai-agent services.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Health check failures, auth cookies, Valkey connectivity, and storage misconfiguration.
</Card>
<Card title="Local development" href="/local-development">
Contributor setup with `deploy/docker-compose.dev.yml` and host-side service runs.
</Card>
</CardGroup>
