Skip to content

Commit d40476d

Browse files
committed
deploy guide
Signed-off-by: Deggen <d.kellenschwiler@bsvassociation.org>
1 parent 9cbf49c commit d40476d

2 files changed

Lines changed: 288 additions & 4 deletions

File tree

DEPLOY.md

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
# Deployment Guide
2+
3+
This guide covers deploying Arcade using Docker Compose or Kubernetes.
4+
5+
## Prerequisites
6+
7+
- A Teranode broadcast URL for the target network
8+
- Arcade container image (`ghcr.io/bsv-blockchain/arcade:<tag>`) or source code to build from
9+
10+
## Configuration Reference
11+
12+
Arcade is configured via environment variables or a `config.yaml` file. Environment variables take precedence.
13+
14+
| Variable | Description | Default |
15+
|---|---|---|
16+
| `ARCADE_NETWORK` | BSV network: `main`, `test`, or `teratestnet` | `main` |
17+
| `ARCADE_STORAGE_PATH` | Root directory for persistent data | `/data` |
18+
| `ARCADE_DATABASE_SQLITE_PATH` | Path to SQLite database file | `/data/arcade.db` |
19+
| `ARCADE_CHAINTRACKS_STORAGE_PATH` | Path for chain header storage | `/data/chaintracks` |
20+
| `ARCADE_TERANODE_BROADCAST_URLS` | Comma-separated Teranode propagation URLs | _(none)_ |
21+
| `ARCADE_SERVER_ADDRESS` | Listen address | `:3011` |
22+
| `ARCADE_LOG_LEVEL` | Log level: `debug`, `info`, `warn`, `error` | `info` |
23+
| `ARCADE_AUTH_ENABLED` | Enable authentication | `false` |
24+
| `ARCADE_AUTH_TOKEN` | Auth bearer token (if auth enabled) | _(none)_ |
25+
26+
See `config.example.yaml` for the full config file format.
27+
28+
## Health Check
29+
30+
The container exposes `GET /health` on port 3011. Use this for readiness/liveness probes and load balancer health checks.
31+
32+
---
33+
34+
## Docker Compose
35+
36+
1. **Create a config file:**
37+
38+
```bash
39+
cp config.example.yaml config.yaml
40+
```
41+
42+
Edit `config.yaml` and set your `teranode.broadcast_urls`.
43+
44+
2. **Start Arcade:**
45+
46+
```bash
47+
docker compose up -d
48+
```
49+
50+
3. **Verify it's running:**
51+
52+
```bash
53+
curl http://localhost:3011/health
54+
docker compose logs -f arcade
55+
```
56+
57+
4. **Stop:**
58+
59+
```bash
60+
docker compose down
61+
```
62+
63+
Data is persisted in the `arcade-data` Docker volume and survives restarts.
64+
65+
### Environment-only configuration (no config file)
66+
67+
If you prefer not to mount a config file, pass everything as environment variables:
68+
69+
```bash
70+
docker run -d \
71+
--name arcade \
72+
-p 3011:3011 \
73+
-e ARCADE_NETWORK=main \
74+
-e ARCADE_TERANODE_BROADCAST_URLS="https://teranode-1.example.com,https://teranode-2.example.com" \
75+
-v arcade-data:/data \
76+
ghcr.io/bsv-blockchain/arcade:v0.1.6
77+
```
78+
79+
---
80+
81+
## Kubernetes
82+
83+
### Concepts
84+
85+
Arcade uses SQLite for storage, so it must run as a **single replica** with a `Recreate` deployment strategy. A `PersistentVolumeClaim` provides durable storage across pod restarts.
86+
87+
### Namespace
88+
89+
Create a namespace for your environment:
90+
91+
```bash
92+
kubectl create namespace arcade
93+
```
94+
95+
### Persistent Volume Claim
96+
97+
```yaml
98+
apiVersion: v1
99+
kind: PersistentVolumeClaim
100+
metadata:
101+
name: arcade-data
102+
namespace: arcade
103+
spec:
104+
accessModes:
105+
- ReadWriteOnce
106+
resources:
107+
requests:
108+
storage: 10Gi
109+
```
110+
111+
Adjust the `storageClassName` and size for your cluster.
112+
113+
### Deployment
114+
115+
```yaml
116+
apiVersion: apps/v1
117+
kind: Deployment
118+
metadata:
119+
name: arcade
120+
namespace: arcade
121+
spec:
122+
selector:
123+
matchLabels:
124+
app: arcade
125+
strategy:
126+
type: Recreate
127+
template:
128+
metadata:
129+
labels:
130+
app: arcade
131+
spec:
132+
securityContext:
133+
fsGroup: 1000
134+
containers:
135+
- name: arcade
136+
image: ghcr.io/bsv-blockchain/arcade:v0.1.6
137+
ports:
138+
- containerPort: 3011
139+
env:
140+
- name: ARCADE_NETWORK
141+
value: "main"
142+
- name: ARCADE_STORAGE_PATH
143+
value: /data
144+
- name: ARCADE_DATABASE_SQLITE_PATH
145+
value: /data/arcade.db
146+
- name: ARCADE_TERANODE_BROADCAST_URLS
147+
value: "https://teranode-1.example.com,https://teranode-2.example.com"
148+
- name: ARCADE_CHAINTRACKS_STORAGE_PATH
149+
value: /data/chaintracks
150+
volumeMounts:
151+
- name: arcade-data
152+
mountPath: /data
153+
livenessProbe:
154+
httpGet:
155+
path: /health
156+
port: 3011
157+
initialDelaySeconds: 10
158+
periodSeconds: 30
159+
readinessProbe:
160+
httpGet:
161+
path: /health
162+
port: 3011
163+
initialDelaySeconds: 5
164+
periodSeconds: 10
165+
securityContext:
166+
runAsNonRoot: true
167+
runAsUser: 1000
168+
allowPrivilegeEscalation: false
169+
capabilities:
170+
drop:
171+
- ALL
172+
volumes:
173+
- name: arcade-data
174+
persistentVolumeClaim:
175+
claimName: arcade-data
176+
```
177+
178+
Key points:
179+
- **`strategy: Recreate`** is required because SQLite does not support concurrent writers.
180+
- **`fsGroup: 1000`** matches the `arcade` user in the container image so the mounted volume is writable.
181+
- Set `ARCADE_NETWORK` to `main`, `test`, or `teratestnet` and point `ARCADE_TERANODE_BROADCAST_URLS` to the appropriate Teranode propagation endpoints for that network.
182+
183+
### Service
184+
185+
```yaml
186+
apiVersion: v1
187+
kind: Service
188+
metadata:
189+
name: arcade
190+
namespace: arcade
191+
spec:
192+
selector:
193+
app: arcade
194+
ports:
195+
- port: 3011
196+
targetPort: 3011
197+
type: ClusterIP
198+
```
199+
200+
### Ingress (optional)
201+
202+
Expose Arcade externally with an Ingress. The example below uses cert-manager for TLS; adapt the annotations and `ingressClassName` for your ingress controller:
203+
204+
```yaml
205+
apiVersion: networking.k8s.io/v1
206+
kind: Ingress
207+
metadata:
208+
name: arcade
209+
namespace: arcade
210+
annotations:
211+
cert-manager.io/cluster-issuer: "letsencrypt"
212+
spec:
213+
ingressClassName: nginx # or traefik, etc.
214+
tls:
215+
- secretName: arcade-tls
216+
hosts:
217+
- arcade.example.com
218+
rules:
219+
- host: arcade.example.com
220+
http:
221+
paths:
222+
- path: /
223+
pathType: Prefix
224+
backend:
225+
service:
226+
name: arcade
227+
port:
228+
number: 3011
229+
```
230+
231+
### Multiple Environments
232+
233+
To run mainnet, testnet, and teratestnet side by side, deploy each into its own namespace with environment-specific values:
234+
235+
| Environment | Namespace | `ARCADE_NETWORK` |
236+
|---|---|---|
237+
| Mainnet | `arcade` | `main` |
238+
| Testnet | `arcade-testnet` | `test` |
239+
| Teratestnet | `arcade-ttn` | `teratestnet` |
240+
241+
Each environment needs its own PVC, Deployment, Service, and (optionally) Ingress. The only differences between environments are the namespace, `ARCADE_NETWORK` value, and `ARCADE_TERANODE_BROADCAST_URLS`.
242+
243+
If you use Kustomize, you can keep the manifests above as a base and create overlays that patch the namespace, network, and broadcast URLs per environment.
244+
245+
---
246+
247+
## Updating
248+
249+
To deploy a new version:
250+
251+
1. Build and push a new image tag (or use a tag published by CI).
252+
2. Update the image reference in your `docker-compose.yaml` or Kubernetes Deployment manifest.
253+
3. Redeploy:
254+
- **Docker Compose:** `docker compose up -d`
255+
- **Kubernetes:** `kubectl apply -f deployment.yaml` or let your GitOps tool reconcile the change.
256+
257+
## Troubleshooting
258+
259+
### Docker Compose
260+
261+
```bash
262+
docker compose logs -f arcade
263+
docker compose exec arcade wget -qO- http://localhost:3011/health
264+
```
265+
266+
### Kubernetes
267+
268+
```bash
269+
kubectl -n arcade get pods
270+
kubectl -n arcade logs deploy/arcade
271+
kubectl -n arcade describe deploy/arcade
272+
kubectl -n arcade get pvc arcade-data
273+
kubectl -n arcade get ingress
274+
```

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,37 +72,47 @@
7272
</td>
7373
</tr>
7474
<tr>
75+
<td align="center">
76+
🐳&nbsp;<a href="DOCKER.md"><code>Docker&nbsp;Guide</code></a>
77+
</td>
78+
<td align="center">
79+
☸️&nbsp;<a href="DEPLOY.md"><code>Deployment&nbsp;Guide</code></a>
80+
</td>
7581
<td align="center">
7682
🏗️&nbsp;<a href="#-architecture"><code>Architecture</code></a>
7783
</td>
84+
</tr>
85+
<tr>
7886
<td align="center">
7987
🧪&nbsp;<a href="#-examples--tests"><code>Examples&nbsp;&&nbsp;Tests</code></a>
8088
</td>
8189
<td align="center">
8290
⚡&nbsp;<a href="#-benchmarks"><code>Benchmarks</code></a>
8391
</td>
84-
</tr>
85-
<tr>
8692
<td align="center">
8793
🛠️&nbsp;<a href="#-code-standards"><code>Code&nbsp;Standards</code></a>
8894
</td>
95+
</tr>
96+
<tr>
8997
<td align="center">
9098
🤖&nbsp;<a href="#-ai-usage--assistant-guidelines"><code>AI&nbsp;Usage</code></a>
9199
</td>
92100
<td align="center">
93101
📚&nbsp;<a href="#-resources"><code>Resources</code></a>
94102
</td>
95-
</tr>
96-
<tr>
97103
<td align="center">
98104
🤝&nbsp;<a href="#-contributing"><code>Contributing</code></a>
99105
</td>
106+
</tr>
107+
<tr>
100108
<td align="center">
101109
👥&nbsp;<a href="#-maintainers"><code>Maintainers</code></a>
102110
</td>
103111
<td align="center">
104112
📝&nbsp;<a href="#-license"><code>License</code></a>
105113
</td>
114+
<td align="center">
115+
</td>
106116
</tr>
107117
</table>
108118
<br/>

0 commit comments

Comments
 (0)