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
6 changes: 6 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
# migrate One-shot Drizzle migrations (used as a k8s initContainer)

ARG NODE_VERSION=22
# The backend's dependency tree has unresolved peer conflicts (e.g.
# x402-stellar wants @stellar/stellar-sdk@^12 while the app uses ^15), so npm
# needs --legacy-peer-deps until that is fixed upstream
# (tracked in https://github.com/SynapsVault/backend/issues). Override with
# --build-arg NPM_FLAGS= once the upstream peer dependency issue is resolved.
ARG NPM_FLAGS=--legacy-peer-deps
# Once upstream peer dependency conflicts (x402-stellar vs @stellar/stellar-sdk)
# are resolved, drop --legacy-peer-deps by passing --build-arg NPM_FLAGS="" or
# leaving NPM_FLAGS empty when the lockfile is clean.
Expand Down
4 changes: 4 additions & 0 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
# that repository; this file lives in the deploy repo.

ARG NODE_VERSION=22
# The frontend repo has no lockfile and conflicting peer dependencies, which
# crash npm's resolver; --legacy-peer-deps works around it until fixed upstream
# (tracked in https://github.com/SynapsVault/frontend/issues).
ARG NPM_FLAGS=--legacy-peer-deps
# Once the upstream lockfile and peer dependencies are resolved, drop the
# --legacy-peer-deps flag (override with --build-arg NPM_FLAGS=--legacy-peer-deps if needed).
ARG NPM_FLAGS=""
Expand Down
32 changes: 32 additions & 0 deletions k8s/backend-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,31 @@ spec:
runAsGroup: 1000
seccompProfile:
type: RuntimeDefault
initContainers:
# Applies pending Drizzle migrations before the new version starts.
- name: migrate
image: backend-migrate
imagePullPolicy: IfNotPresent
envFrom:
- configMapRef:
name: backend-config
- secretRef:
name: backend-secret
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: tmp
mountPath: /tmp
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
containers:
- name: backend
image: backend
Expand All @@ -44,8 +69,12 @@ spec:
name: backend-secret
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: tmp
mountPath: /tmp
startupProbe:
httpGet:
path: /health/live
Expand Down Expand Up @@ -76,3 +105,6 @@ spec:
limits:
cpu: "1000m"
memory: "512Mi"
volumes:
- name: tmp
emptyDir: {}
23 changes: 23 additions & 0 deletions k8s/frontend-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ spec:
labels:
app: frontend
spec:
securityContext:
runAsNonRoot: true
runAsUser: 101
runAsGroup: 101
seccompProfile:
type: RuntimeDefault
containers:
- name: frontend
image: frontend
Expand All @@ -29,6 +35,16 @@ spec:
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: nginx-cache
mountPath: /var/cache/nginx
- name: nginx-run
mountPath: /var/run
- name: tmp
mountPath: /tmp
startupProbe:
httpGet:
path: /
Expand Down Expand Up @@ -59,6 +75,13 @@ spec:
limits:
cpu: "250m"
memory: "128Mi"
volumes:
- name: nginx-cache
emptyDir: {}
- name: nginx-run
emptyDir: {}
- name: tmp
emptyDir: {}
---
apiVersion: v1
kind: Service
Expand Down
1 change: 1 addition & 0 deletions k8s/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ resources:
- backend-deployment.yaml
- backend-service.yaml
- frontend-deployment.yaml
- pdb.yaml
- ingress.yaml

labels:
Expand Down
23 changes: 23 additions & 0 deletions k8s/pdb.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: backend
labels:
app: backend
spec:
minAvailable: 1
selector:
matchLabels:
app: backend
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: frontend
labels:
app: frontend
spec:
minAvailable: 1
selector:
matchLabels:
app: frontend
33 changes: 26 additions & 7 deletions scripts/restore.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
# from the backup came back, then drop it
# --work-dir <dir> Working directory for downloads (default: mktemp -d)
# --keep-work-dir Do not delete the working directory on exit
# --post-restore-sql <f> Optional SQL script to run against Postgres after
# restoring (aliases $CONTRACT_STATE_FILE). Note: this
# is a PostgreSQL .sql script, NOT the on-chain JSON
# snapshots under contracts/ in the backup archive.
# --skip-contract-state Do not apply CONTRACT_STATE_FILE / --post-restore-sql
# --skip-contract-state Do not re-apply CONTRACT_STATE_FILE after restore
# -y, --yes Skip interactive confirmation when restoring over a
# non-throwaway target database
Expand All @@ -32,7 +37,10 @@
# BACKUP_ENCRYPTION_KEY gpg passphrase used by backup.sh
# BACKUP_AGE_IDENTITY_FILE age identity file, for age-encrypted backups
# DATABASE_URL Default target database connection string
# CONTRACT_STATE_FILE Optional SQL file to apply after restore
# CONTRACT_STATE_FILE Optional post-restore PostgreSQL .sql file
# (e.g. to update contract ID rows in DB;
# for repointing env/ConfigMap contract IDs,
# use scripts/rollback-contracts.cjs instead)
#
# Exit codes:
# 0 success
Expand All @@ -56,6 +64,7 @@ die() {
}

usage() {
sed -n '2,47p' "$0" | sed 's/^# \{0,1\}//'
sed -n '2,43p' "$0" | sed 's/^# \{0,1\}//'
}

Expand All @@ -69,6 +78,7 @@ ASSUME_YES="false"
WORK_DIR=""
KEEP_WORK_DIR="false"
SKIP_CONTRACT_STATE="false"
POST_RESTORE_SQL_ARG=""

while [[ $# -gt 0 ]]; do
case "$1" in
Expand Down Expand Up @@ -99,6 +109,11 @@ while [[ $# -gt 0 ]]; do
KEEP_WORK_DIR="true"
shift
;;
--post-restore-sql)
[[ $# -ge 2 ]] || die 1 "--post-restore-sql requires a path to a .sql file"
POST_RESTORE_SQL_ARG="$2"
shift 2
;;
--skip-contract-state)
SKIP_CONTRACT_STATE="true"
shift
Expand Down Expand Up @@ -349,16 +364,20 @@ restore_into() {
apply_contract_state() {
local url="$1"
if [[ "$SKIP_CONTRACT_STATE" == "true" ]]; then
log "skipping contract state re-application (--skip-contract-state)"
log "skipping post-restore SQL application (--skip-contract-state)"
return 0
fi
if [[ -z "$CONTRACT_STATE_FILE" ]]; then
log "no CONTRACT_STATE_FILE configured; skipping contract state re-application"
local sql_file="${POST_RESTORE_SQL_ARG:-${CONTRACT_STATE_FILE:-}}"
if [[ -z "$sql_file" ]]; then
log "no CONTRACT_STATE_FILE / --post-restore-sql configured; skipping post-restore SQL"
return 0
fi
[[ -f "$CONTRACT_STATE_FILE" ]] || die 4 "contract state file not found: $CONTRACT_STATE_FILE"
log "re-applying contract state from $CONTRACT_STATE_FILE"
psql_exec "$url" -f "$CONTRACT_STATE_FILE" || die 4 "failed to re-apply contract state"
[[ -f "$sql_file" ]] || die 4 "post-restore SQL file not found: $sql_file"
if [[ "$sql_file" == *.json ]]; then
die 1 "CONTRACT_STATE_FILE ($sql_file) must be a PostgreSQL .sql script, not a JSON contract snapshot (use scripts/rollback-contracts.cjs to repoint on-chain contract IDs)"
fi
log "applying post-restore SQL from $sql_file"
psql_exec "$url" -f "$sql_file" || die 4 "failed to apply post-restore SQL from $sql_file"
}

# ---------------------------------------------------------------------------
Expand Down