Add standalone docker-compose for all 20 modules

Each module can run as an independent container using its standalone.ts
entrypoint. Reuses the same rspace-online image with CMD override.

Usage:
  docker compose -f docker-compose.yml -f docker-compose.standalone.yml \
    up -d rtrips-standalone

All services share rspace-db and traefik-public. Module-specific deps
mapped: books/files/swag volumes, OSRM for trips, IMAP for inbox,
R2 for tube, payment network for cart/funds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-02-22 03:07:09 +00:00
parent d25bb3ec5e
commit 3c986f1709
1 changed files with 327 additions and 0 deletions

View File

@ -0,0 +1,327 @@
# Standalone module deployments — each module runs as its own container.
#
# Usage:
# docker compose -f docker-compose.yml -f docker-compose.standalone.yml up -d rbooks-standalone
#
# All services reuse the same rspace-online image (built by the main compose).
# They share the rspace-db database and traefik-public network.
# To deploy ALL standalone modules at once:
# docker compose -f docker-compose.yml -f docker-compose.standalone.yml up -d
#
# NOTE: Standalone services override the default CMD to run standalone.ts.
# The unified rspace-online container still serves all domains by default.
# To switch a domain from unified to standalone, remove its Traefik label
# from the main compose and bring up its standalone service here.
x-standalone-base: &standalone-base
image: rspace-online-rspace:latest
restart: unless-stopped
environment: &base-env
NODE_ENV: production
PORT: "3000"
DATABASE_URL: postgres://rspace:${POSTGRES_PASSWORD:-rspace}@rspace-db:5432/rspace
depends_on:
rspace-db:
condition: service_healthy
networks:
- traefik-public
- rspace-internal
x-traefik-labels: &traefik-enabled
traefik.enable: "true"
services:
# ── rBooks ──
rbooks-standalone:
<<: *standalone-base
container_name: rbooks-standalone
command: ["bun", "run", "modules/books/standalone.ts"]
volumes:
- rspace-books:/data/books
environment:
<<: *base-env
BOOKS_DIR: /data/books
labels:
<<: *traefik-enabled
traefik.http.routers.rbooks-sa.rule: Host(`rbooks.online`)
traefik.http.routers.rbooks-sa.entrypoints: web
traefik.http.services.rbooks-sa.loadbalancer.server.port: "3000"
# ── rPubs ──
rpubs-standalone:
<<: *standalone-base
container_name: rpubs-standalone
command: ["bun", "run", "modules/pubs/standalone.ts"]
labels:
<<: *traefik-enabled
traefik.http.routers.rpubs-sa.rule: Host(`rpubs.online`)
traefik.http.routers.rpubs-sa.entrypoints: web
traefik.http.services.rpubs-sa.loadbalancer.server.port: "3000"
# ── rCart ──
rcart-standalone:
<<: *standalone-base
container_name: rcart-standalone
command: ["bun", "run", "modules/cart/standalone.ts"]
environment:
<<: *base-env
FLOW_SERVICE_URL: http://payment-flow:3010
FLOW_ID: a79144ec-e6a2-4e30-a42a-6d8237a5953d
FUNNEL_ID: 0ff6a9ac-1667-4fc7-9a01-b1620810509f
X402_PAY_TO: ${X402_PAY_TO:-}
X402_NETWORK: ${X402_NETWORK:-eip155:84532}
X402_UPLOAD_PRICE: ${X402_UPLOAD_PRICE:-0.01}
X402_FACILITATOR_URL: ${X402_FACILITATOR_URL:-https://x402.org/facilitator}
networks:
- traefik-public
- rspace-internal
- payment-network
labels:
<<: *traefik-enabled
traefik.http.routers.rcart-sa.rule: Host(`rcart.online`)
traefik.http.routers.rcart-sa.entrypoints: web
traefik.http.services.rcart-sa.loadbalancer.server.port: "3000"
# ── rProviders ──
rproviders-standalone:
<<: *standalone-base
container_name: rproviders-standalone
command: ["bun", "run", "modules/providers/standalone.ts"]
labels:
<<: *traefik-enabled
traefik.http.routers.rproviders-sa.rule: Host(`providers.mycofi.earth`)
traefik.http.routers.rproviders-sa.entrypoints: web
traefik.http.services.rproviders-sa.loadbalancer.server.port: "3000"
# ── rSwag ──
rswag-standalone:
<<: *standalone-base
container_name: rswag-standalone
command: ["bun", "run", "modules/swag/standalone.ts"]
volumes:
- rspace-swag:/data/swag-artifacts
environment:
<<: *base-env
SWAG_ARTIFACTS_DIR: /data/swag-artifacts
labels:
<<: *traefik-enabled
traefik.http.routers.rswag-sa.rule: Host(`swag.mycofi.earth`)
traefik.http.routers.rswag-sa.entrypoints: web
traefik.http.services.rswag-sa.loadbalancer.server.port: "3000"
# ── rChoices ──
rchoices-standalone:
<<: *standalone-base
container_name: rchoices-standalone
command: ["bun", "run", "modules/choices/standalone.ts"]
labels:
<<: *traefik-enabled
traefik.http.routers.rchoices-sa.rule: Host(`rchoices.online`)
traefik.http.routers.rchoices-sa.entrypoints: web
traefik.http.services.rchoices-sa.loadbalancer.server.port: "3000"
# ── rFunds ──
rfunds-standalone:
<<: *standalone-base
container_name: rfunds-standalone
command: ["bun", "run", "modules/funds/standalone.ts"]
environment:
<<: *base-env
FLOW_SERVICE_URL: http://payment-flow:3010
FLOW_ID: a79144ec-e6a2-4e30-a42a-6d8237a5953d
FUNNEL_ID: 0ff6a9ac-1667-4fc7-9a01-b1620810509f
networks:
- traefik-public
- rspace-internal
- payment-network
labels:
<<: *traefik-enabled
traefik.http.routers.rfunds-sa.rule: Host(`rfunds.online`)
traefik.http.routers.rfunds-sa.entrypoints: web
traefik.http.services.rfunds-sa.loadbalancer.server.port: "3000"
# ── rFiles ──
rfiles-standalone:
<<: *standalone-base
container_name: rfiles-standalone
command: ["bun", "run", "modules/files/standalone.ts"]
volumes:
- rspace-files:/data/files
environment:
<<: *base-env
FILES_DIR: /data/files
labels:
<<: *traefik-enabled
traefik.http.routers.rfiles-sa.rule: Host(`rfiles.online`)
traefik.http.routers.rfiles-sa.entrypoints: web
traefik.http.services.rfiles-sa.loadbalancer.server.port: "3000"
# ── rForum ──
rforum-standalone:
<<: *standalone-base
container_name: rforum-standalone
command: ["bun", "run", "modules/forum/standalone.ts"]
environment:
<<: *base-env
HETZNER_API_TOKEN: ${HETZNER_API_TOKEN}
CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN}
CLOUDFLARE_ZONE_ID: ${CLOUDFLARE_ZONE_ID}
labels:
<<: *traefik-enabled
traefik.http.routers.rforum-sa.rule: Host(`rforum.online`)
traefik.http.routers.rforum-sa.entrypoints: web
traefik.http.services.rforum-sa.loadbalancer.server.port: "3000"
# ── rVote ──
rvote-standalone:
<<: *standalone-base
container_name: rvote-standalone
command: ["bun", "run", "modules/vote/standalone.ts"]
labels:
<<: *traefik-enabled
traefik.http.routers.rvote-sa.rule: Host(`rvote.online`)
traefik.http.routers.rvote-sa.entrypoints: web
traefik.http.services.rvote-sa.loadbalancer.server.port: "3000"
# ── rNotes ──
rnotes-standalone:
<<: *standalone-base
container_name: rnotes-standalone
command: ["bun", "run", "modules/notes/standalone.ts"]
labels:
<<: *traefik-enabled
traefik.http.routers.rnotes-sa.rule: Host(`rnotes.online`)
traefik.http.routers.rnotes-sa.entrypoints: web
traefik.http.services.rnotes-sa.loadbalancer.server.port: "3000"
# ── rMaps ──
rmaps-standalone:
<<: *standalone-base
container_name: rmaps-standalone
command: ["bun", "run", "modules/maps/standalone.ts"]
environment:
<<: *base-env
MAPS_SYNC_URL: wss://sync.rmaps.online
labels:
<<: *traefik-enabled
traefik.http.routers.rmaps-sa.rule: Host(`rmaps.online`)
traefik.http.routers.rmaps-sa.entrypoints: web
traefik.http.services.rmaps-sa.loadbalancer.server.port: "3000"
# ── rWork ──
rwork-standalone:
<<: *standalone-base
container_name: rwork-standalone
command: ["bun", "run", "modules/work/standalone.ts"]
labels:
<<: *traefik-enabled
traefik.http.routers.rwork-sa.rule: Host(`rwork.online`)
traefik.http.routers.rwork-sa.entrypoints: web
traefik.http.services.rwork-sa.loadbalancer.server.port: "3000"
# ── rTrips ──
rtrips-standalone:
<<: *standalone-base
container_name: rtrips-standalone
command: ["bun", "run", "modules/trips/standalone.ts"]
environment:
<<: *base-env
OSRM_URL: http://osrm-backend:5000
labels:
<<: *traefik-enabled
traefik.http.routers.rtrips-sa.rule: Host(`rtrips.online`)
traefik.http.routers.rtrips-sa.entrypoints: web
traefik.http.services.rtrips-sa.loadbalancer.server.port: "3000"
# ── rCal ──
rcal-standalone:
<<: *standalone-base
container_name: rcal-standalone
command: ["bun", "run", "modules/cal/standalone.ts"]
labels:
<<: *traefik-enabled
traefik.http.routers.rcal-sa.rule: Host(`rcal.online`)
traefik.http.routers.rcal-sa.entrypoints: web
traefik.http.services.rcal-sa.loadbalancer.server.port: "3000"
# ── rNetwork ──
rnetwork-standalone:
<<: *standalone-base
container_name: rnetwork-standalone
command: ["bun", "run", "modules/network/standalone.ts"]
environment:
<<: *base-env
TWENTY_API_URL: https://rnetwork.online
TWENTY_API_TOKEN: ${TWENTY_API_TOKEN}
labels:
<<: *traefik-enabled
traefik.http.routers.rnetwork-sa.rule: Host(`rnetwork.online`)
traefik.http.routers.rnetwork-sa.entrypoints: web
traefik.http.services.rnetwork-sa.loadbalancer.server.port: "3000"
# ── rTube ──
rtube-standalone:
<<: *standalone-base
container_name: rtube-standalone
command: ["bun", "run", "modules/tube/standalone.ts"]
environment:
<<: *base-env
R2_ENDPOINT: ${R2_ENDPOINT}
R2_BUCKET: ${R2_BUCKET:-rtube-videos}
R2_ACCESS_KEY_ID: ${R2_ACCESS_KEY_ID}
R2_SECRET_ACCESS_KEY: ${R2_SECRET_ACCESS_KEY}
R2_PUBLIC_URL: ${R2_PUBLIC_URL}
labels:
<<: *traefik-enabled
traefik.http.routers.rtube-sa.rule: Host(`rtube.online`)
traefik.http.routers.rtube-sa.entrypoints: web
traefik.http.services.rtube-sa.loadbalancer.server.port: "3000"
# ── rInbox ──
rinbox-standalone:
<<: *standalone-base
container_name: rinbox-standalone
command: ["bun", "run", "modules/inbox/standalone.ts"]
environment:
<<: *base-env
IMAP_HOST: mail.rmail.online
IMAP_PORT: "993"
IMAP_TLS_REJECT_UNAUTHORIZED: "false"
networks:
- traefik-public
- rspace-internal
- rmail-mailcow
labels:
<<: *traefik-enabled
traefik.http.routers.rinbox-sa.rule: Host(`rinbox.online`)
traefik.http.routers.rinbox-sa.entrypoints: web
traefik.http.services.rinbox-sa.loadbalancer.server.port: "3000"
# ── rData ──
rdata-standalone:
<<: *standalone-base
container_name: rdata-standalone
command: ["bun", "run", "modules/data/standalone.ts"]
environment:
<<: *base-env
UMAMI_URL: https://analytics.rspace.online
UMAMI_WEBSITE_ID: 292f6ac6-79f8-497b-ba6a-7a51e3b87b9f
labels:
<<: *traefik-enabled
traefik.http.routers.rdata-sa.rule: Host(`rdata.online`)
traefik.http.routers.rdata-sa.entrypoints: web
traefik.http.services.rdata-sa.loadbalancer.server.port: "3000"
# ── rWallet ──
rwallet-standalone:
<<: *standalone-base
container_name: rwallet-standalone
command: ["bun", "run", "modules/wallet/standalone.ts"]
labels:
<<: *traefik-enabled
traefik.http.routers.rwallet-sa.rule: Host(`rwallet.online`)
traefik.http.routers.rwallet-sa.entrypoints: web
traefik.http.services.rwallet-sa.loadbalancer.server.port: "3000"
# Volumes and networks inherited from main docker-compose.yml
# Use: docker compose -f docker-compose.yml -f docker-compose.standalone.yml up -d <service>