Docker Compose under [production/docker](./production/docker) runs the full stack. Build context is the [psf-memo](https://github.com/Permissionless-Software-Foundation/psf-memo) monorepo root; each image copies its package (`psf-memo-db`, `psf-memo-indexer`, or `psf-memo-client`) from the local checkout.
LevelDB data persists on the host at `production/data/leveldb`.
### Prerequisites
- Docker Engine and Docker Compose v2 (`docker compose`)
- A Bitcoin Cash full node with RPC and ZMQ reachable from the containers
- On a typical Linux Docker host, the bridge gateway `172.17.0.1` reaches services on the host (RPC, ZMQ, and sibling containers published on host ports)
### 1. Configure environment files
```bash
cd production/docker
cp memo-db/.env-example memo-db/.env
cp block-indexer/.env-example block-indexer/.env
cp tx-indexer/.env-example tx-indexer/.env
cp memo-client/.env-example memo-client/.env
```
Edit each `.env` before building or starting.
#### `memo-db/.env`
| Variable | Typical value | Description |
|----------|---------------|-------------|
| `PORT` | `5021` | REST API listen port |
| `SVC_ENV` | `prod` | Runtime environment |
| `BACKUP_QTY` | `3` | How many epoch zip backups to keep |
| `EXIT_ON_MISSING_BACKUP` | `false` | Exit if expected backup is missing |
#### `block-indexer/.env` and `tx-indexer/.env`
| Variable | Typical Docker value | Description |
|----------|----------------------|-------------|
| `PSF_MEMO_DB_URL` | `http://172.17.0.1:5021` | URL of `memo-db` from inside the container |
Use a hostname or IP your containers can actually reach for RPC, ZMQ, and `memo-db`. `172.17.0.1` is the usual Docker bridge address when those services are published on the host.
#### `memo-client/.env`
| Variable | Example | Description |
|----------|---------|-------------|
| `REACT_APP_MEMO_DB_URL` | `http://localhost:5021` | Browser-facing base URL of `memo-db` (no trailing slash) |
This value is **baked into the SPA at image build time**. Create React App reads it from `memo-client/.env` during `npm run build`.
- Local / same-machine browser: `http://localhost:5021` (or `http://<host-ip>:5021`)
- Separate domains: `https://api.mydomain.com` when the client is at `https://client.mydomain.com`
Changing `REACT_APP_MEMO_DB_URL` requires rebuilding the `memo-client` image (see below).
### 2. Build images
```bash
cd production/docker
docker compose build
```
Rebuild a single service after changing its Dockerfile or (for the client) `.env`:
```bash
docker compose build --no-cache memo-client
docker compose build block-indexer
docker compose build tx-indexer
docker compose build memo-db
```
`block-indexer` and `tx-indexer` use explicit image names (`memo-block-indexer`, `memo-tx-indexer`) so they do not collide with similarly named images from other projects (for example `psf-slp-indexer-g2`).
### 3. Start the stack
Preferred order: database first, then indexers, then the client.
```bash
cd production/docker
docker compose up -d memo-db
docker compose up -d block-indexer tx-indexer
docker compose up -d memo-client
```
Or start everything at once:
```bash
docker compose up -d
```
### 4. Verify
| Check | URL / command |
|-------|----------------|
| Database API / docs | http://localhost:5021/ |
| Database health | http://localhost:5021/health |
# Rebuild and recreate after config or image changes
docker compose up -d --build memo-client
```
Start scripts under each service directory (`start-*.sh`) and `.env` files are bind-mounted into the containers. Edit them on the host and restart the service (no rebuild) unless you changed something that only applies at image build time (notably `memo-client/.env`).
### Production domains example
When the client and API are on different hostnames:
1. Set `memo-client/.env`:
```bash
REACT_APP_MEMO_DB_URL=https://api.mydomain.com
```
2. Rebuild and redeploy the client:
```bash
docker compose build --no-cache memo-client
docker compose up -d memo-client
```
3. Reverse-proxy:
- `client.mydomain.com` → host port `3000` (`memo-client`)
- `api.mydomain.com` → host port `5021` (`memo-db`)
`memo-db` enables CORS with `origin: '*'`, so the browser may call the API from another subdomain once HTTPS and DNS are in place.