Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ name: cd
# <dockerhub-username>/paca-ai-agent:<tag> — AI Agent service Docker image
# registry.npmjs.org @paca-ai/paca-mcp — MCP server npm package
# registry.npmjs.org @paca-ai/plugin-sdk-react — Plugin frontend SDK npm package
# GitHub Release assets:
# install.sh — one-shot interactive install script
# docker-compose.yml — standalone compose (no source tree required)
# gateway.conf — nginx gateway configuration

on:
release:
Expand Down Expand Up @@ -305,3 +309,44 @@ jobs:
else
npm publish --provenance --access public
fi

# ─────────────────────────────────────────────────────────────────────────────
# Deployment assets → GitHub Release
#
# Uploads three files so that users can run Paca without cloning the repo:
# install.sh — interactive setup wizard (download + configure + start)
# docker-compose.yml — standalone compose referencing pre-built DockerHub images
# gateway.conf — nginx gateway configuration required by the compose file
#
# End-users download and run:
# curl -fsSL https://github.com/Paca-AI/paca/releases/latest/download/install.sh -o install.sh
# bash install.sh
# ─────────────────────────────────────────────────────────────────────────────
release-assets:
name: Upload deployment assets to release
runs-on: ubuntu-latest
needs: [api-image, realtime-image, web-image, ai-agent-image, publish-mcp]
permissions:
contents: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Prepare assets
run: |
# Rename files to the names end-users will download.
cp deploy/docker-compose.prod.yml docker-compose.yml
cp deploy/nginx/gateway.conf gateway.conf
cp scripts/install.sh install.sh
chmod +x install.sh

- name: Upload assets to GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "${{ github.event.release.tag_name }}" \
install.sh \
docker-compose.yml \
gateway.conf \
--clobber
197 changes: 134 additions & 63 deletions deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,149 @@
This directory contains deployment assets for two distinct use cases:

- contributor-friendly local development;
- production-oriented container deployment examples.

Keeping those concerns separate makes the repository easier to understand and avoids presenting a local-only stack as a production recommendation.
- production container deployment for self-hosters.

## Contents

| File | Description |
|---|---|
| `docker-compose.dev.yml` | Local development stack: PostgreSQL, Valkey, MinIO, and optional app containers |
| `docker-compose.prod.yml` | Production-oriented single-host stack: web, API, PostgreSQL, Valkey, and MinIO |
| `docker-compose.prod.yml` | Production stack: pulls pre-built images from DockerHub, no source checkout required |
| `docker-compose.e2e.yml` | End-to-end test stack mirroring production topology with fixed test credentials |
| `.env.dev.example` | Optional environment file for `docker-compose.dev.yml` (tunnel / custom domain) |
| `.env.production.example` | Example environment file for `docker-compose.prod.yml` |
| `.env.production.example` | Example environment file for manual production deployments |

Service container definitions live with each service:
- [`services/api/Dockerfile`](../services/api/Dockerfile)
- [`apps/web/Dockerfile`](../apps/web/Dockerfile)

## Production Deployment

### Recommended: install script

The easiest way to run Paca without cloning the repository is via the install script
published with each release. It downloads the compose file and nginx config, walks you
through configuration interactively (database, storage, AI agent), generates a `.env`
with strong random secrets, and starts the stack.

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

The installer supports:

| Option | Description |
|---|---|
| Bundled PostgreSQL | Starts a postgres container (default) |
| External PostgreSQL | Supply a `DATABASE_URL`; postgres container is suppressed |
| Self-hosted MinIO | Starts a MinIO container for S3-compatible file storage (default) |
| AWS S3 | Supply AWS credentials; MinIO container is suppressed |
| AI Agent | Enabled by default; can be skipped to reduce resource usage |

### Manual setup

Download the two required files from the latest release:

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

Download the example environment file and edit it:

```bash
curl -fsSL https://github.com/Paca-AI/paca/releases/latest/download/docker-compose.yml -o docker-compose.yml
# Or use the .env.production.example from the repo as a reference:
# https://github.com/Paca-AI/paca/blob/master/deploy/.env.production.example
```

Create a `.env` with the required variables:

```bash
# Required: generate with 'openssl rand -hex 32'
JWT_SECRET=<strong-random-secret>
ADMIN_PASSWORD=<strong-password>
POSTGRES_PASSWORD=<strong-random-password>
# Required when using AI agent: generate with 'openssl rand -hex 32'
AGENT_API_KEY=<strong-random-secret>
INTERNAL_API_KEY=<strong-random-secret>
# Required for plugin secrets at rest: generate with 'openssl rand -hex 32'
ENCRYPTION_KEY=<64-char-hex>
PUBLIC_URL=http://your-domain-or-ip
```

Start the full stack (bundled PostgreSQL + MinIO):

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

**With external PostgreSQL** (suppress the bundled container):

```bash
# Set DATABASE_URL in .env to your managed connection string.
docker compose --env-file .env up -d --scale postgres=0
```

**With AWS S3** (suppress MinIO):

```bash
# Set STORAGE_PROVIDER=s3 and real AWS credentials in .env.
docker compose --env-file .env up -d --scale minio=0
```

**Without the AI agent**:

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

Flags can be combined:

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

### Upgrading from an earlier installation

The compose project was renamed from `paca-prod` to `paca` in this release.
Docker Compose namespaces volumes by project name, so existing volumes
(`paca-prod_postgres_data`, `paca-prod_minio_data`, etc.) are **not** automatically
attached to the new stack. To migrate:

```bash
# 1. Stop the old stack (volumes are preserved on disk).
docker compose -p paca-prod --env-file .env down

# 2. Rename each volume you want to keep.
docker volume create paca_postgres_data
docker run --rm \
-v paca-prod_postgres_data:/from \
-v paca_postgres_data:/to \
alpine sh -c "cp -av /from/. /to/"
docker volume rm paca-prod_postgres_data

# Repeat for minio_data, valkey_data, and plugin volumes as needed.

# 3. Start the new stack.
docker compose --env-file .env up -d
```

If you are doing a fresh install (no data to keep), no migration is needed.

### Pinning a release version

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

```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
```

## Development Compose

Use [`docker-compose.dev.yml`](./docker-compose.dev.yml) for local development and contributor onboarding.
Expand All @@ -45,9 +170,8 @@ Start only shared dependencies:
docker compose -f deploy/docker-compose.dev.yml up -d postgres valkey
```

For day-to-day coding, contributors can still run the application services directly on the host and use Docker Compose only for PostgreSQL and Valkey.

The Postgres schema is applied automatically on the first container start from `services/api/migrations/`.
For day-to-day coding, contributors can run the application services directly on the host
and use Docker Compose only for PostgreSQL and Valkey.

### Development service ports

Expand All @@ -72,67 +196,14 @@ Remove the Postgres volume as well:
docker compose -f deploy/docker-compose.dev.yml down -v
```

## Production Compose

Use [`docker-compose.prod.yml`](./docker-compose.prod.yml) as a self-hosting baseline for open-source deployments.

The production compose includes PostgreSQL and Valkey because a public repository should offer a runnable end-to-end deployment path. It is still a single-host baseline rather than a universal recommendation. Teams using managed services can keep the same application images and point the runtime configuration at external infrastructure instead.

Create a production environment file from the example:

```bash
cp deploy/.env.production.example deploy/.env.production
```

Then run:

**With MinIO (default self-hosted):**
```bash
docker compose \
--env-file deploy/.env.production \
-f deploy/docker-compose.prod.yml up -d --build
```

**With AWS S3 (suppress MinIO):**
```bash
# Set STORAGE_PROVIDER=s3 and real AWS credentials in .env.production
docker compose \
--env-file deploy/.env.production \
-f deploy/docker-compose.prod.yml up -d --build --scale minio=0
```

This file is suitable as:

- a self-hosting starting point;
- a CI/CD handoff artifact;
- a reference for container image names and required runtime configuration.

By default, the web and API services are published to the host in the production compose. PostgreSQL, Valkey, and MinIO stay on the internal Compose network unless an operator intentionally exposes them.

## E2E Compose

Use [`docker-compose.e2e.yml`](./docker-compose.e2e.yml) to spin up a full production-like stack with fixed, test-safe credentials for running end-to-end tests:
Use [`docker-compose.e2e.yml`](./docker-compose.e2e.yml) to spin up a full production-like
stack with fixed, test-safe credentials for running end-to-end tests:

```bash
docker compose -f deploy/docker-compose.e2e.yml up -d --build --wait
docker compose -f deploy/docker-compose.e2e.yml down -v
```

All secrets are intentionally weak and public — never use them outside a local E2E environment.

## Object Storage

All environments include MinIO, an S3-compatible object store, to support file attachments without requiring an AWS account.

In production, MinIO runs by default. When using AWS S3, pass `--scale minio=0` to suppress the MinIO container:

```bash
docker compose --env-file deploy/.env.production \
-f deploy/docker-compose.prod.yml up -d --build --scale minio=0
```

To switch to AWS S3:
1. Set `STORAGE_PROVIDER=s3` in `.env.production`.
2. Leave `STORAGE_ENDPOINT` empty (uses the default AWS regional endpoint) or set it explicitly.
3. Supply real `STORAGE_ACCESS_KEY_ID` and `STORAGE_SECRET_ACCESS_KEY`.
4. Add `--scale minio=0` to the startup command.
Loading
Loading