Skip to content

Add Docker healthchecks for core services#249

Merged
macterra merged 1 commit intomainfrom
248-add-docker-healthchecks
Mar 21, 2026
Merged

Add Docker healthchecks for core services#249
macterra merged 1 commit intomainfrom
248-add-docker-healthchecks

Conversation

@macterra
Copy link
Collaborator

Summary

  • add Docker healthchecks for core Archon services and supporting UIs
  • switch relevant compose dependencies to where startup depends on readiness
  • add a endpoint for the hyperswarm mediator so health reflects upstream readiness

Validation

  • name: archon
    services:
    btc-mainnet-mediator:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.satoshi
    args:
    GIT_COMMIT: unknown
    depends_on:
    btc-mainnet-wallet:
    condition: service_started
    required: true
    gatekeeper:
    condition: service_started
    required: true
    keymaster:
    condition: service_started
    required: true
    environment:
    ARCHON_ADMIN_API_KEY: DO3E06-un0cBMxdHXxYy8TxnjlgOlO9p87-CVJKTPj49Mw==
    ARCHON_GATEKEEPER_URL: http://gatekeeper:4224
    ARCHON_KEYMASTER_URL: http://keymaster:4226
    ARCHON_MONGODB_URL: mongodb://mongodb:27017
    ARCHON_NODE_ID: Wolverine
    ARCHON_REDIS_URL: redis://redis:6379
    ARCHON_SAT_CHAIN: BTC:mainnet
    ARCHON_SAT_DB: json
    ARCHON_SAT_EXPORT_INTERVAL: "1"
    ARCHON_SAT_FEE_BLOCK_TARGET: "1"
    ARCHON_SAT_FEE_FALLBACK_SAT_BYTE: "10"
    ARCHON_SAT_FEE_MAX: "0.00010000"
    ARCHON_SAT_FEE_ORACLE_URL: https://mempool.space/api/v1/fees/recommended
    ARCHON_SAT_HOST: taranis
    ARCHON_SAT_IMPORT_INTERVAL: "1"
    ARCHON_SAT_METRICS_PORT: "4234"
    ARCHON_SAT_NETWORK: bitcoin
    ARCHON_SAT_PASS: OiNHh1VH5oacjAmpIg8yTPgROoiLVBiGbVyG634gxHE=
    ARCHON_SAT_PORT: "8332"
    ARCHON_SAT_RBF_ENABLED: "true"
    ARCHON_SAT_REIMPORT: "true"
    ARCHON_SAT_START_BLOCK: "934000"
    ARCHON_SAT_USER: umbrel
    ARCHON_WALLET_URL: http://btc-mainnet-wallet:4242
    image: ghcr.io/archetech/satoshi-mediator
    networks:
    default: null
    ports:
    - mode: ingress
    host_ip: 127.0.0.1
    target: 4234
    published: "4234"
    protocol: tcp
    user: 1000:1000
    volumes:
    - type: bind
    source: /home/david/archetech/archon/data
    target: /app/satoshi/data
    bind:
    create_host_path: true
    btc-mainnet-wallet:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.wallet
    args:
    GIT_COMMIT: unknown
    depends_on:
    keymaster:
    condition: service_started
    required: true
    environment:
    ARCHON_ADMIN_API_KEY: DO3E06-un0cBMxdHXxYy8TxnjlgOlO9p87-CVJKTPj49Mw==
    ARCHON_KEYMASTER_URL: http://keymaster:4226
    ARCHON_NODE_ID: Wolverine
    ARCHON_WALLET_BTC_HOST: taranis
    ARCHON_WALLET_BTC_PASS: OiNHh1VH5oacjAmpIg8yTPgROoiLVBiGbVyG634gxHE=
    ARCHON_WALLET_BTC_PORT: "8332"
    ARCHON_WALLET_BTC_USER: umbrel
    ARCHON_WALLET_METRICS_PORT: "4243"
    ARCHON_WALLET_NETWORK: mainnet
    ARCHON_WALLET_PORT: "4242"
    image: ghcr.io/archetech/wallet
    networks:
    default: null
    ports:
    - mode: ingress
    host_ip: 127.0.0.1
    target: 4242
    published: "4242"
    protocol: tcp
    - mode: ingress
    host_ip: 127.0.0.1
    target: 4243
    published: "4243"
    protocol: tcp
    user: 1000:1000
    btc-signet-mediator:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.satoshi
    args:
    GIT_COMMIT: unknown
    depends_on:
    btc-signet-node:
    condition: service_started
    required: true
    btc-signet-wallet:
    condition: service_started
    required: true
    gatekeeper:
    condition: service_started
    required: true
    keymaster:
    condition: service_started
    required: true
    environment:
    ARCHON_ADMIN_API_KEY: DO3E06-un0cBMxdHXxYy8TxnjlgOlO9p87-CVJKTPj49Mw==
    ARCHON_GATEKEEPER_URL: http://gatekeeper:4224
    ARCHON_KEYMASTER_URL: http://keymaster:4226
    ARCHON_MONGODB_URL: mongodb://mongodb:27017
    ARCHON_NODE_ID: Wolverine
    ARCHON_REDIS_URL: redis://redis:6379
    ARCHON_SAT_CHAIN: BTC:signet
    ARCHON_SAT_DB: json
    ARCHON_SAT_EXPORT_INTERVAL: "1"
    ARCHON_SAT_FEE_BLOCK_TARGET: "1"
    ARCHON_SAT_FEE_FALLBACK_SAT_BYTE: "10"
    ARCHON_SAT_FEE_MAX: "0.00003000"
    ARCHON_SAT_FEE_ORACLE_URL: ""
    ARCHON_SAT_HOST: btc-signet-node
    ARCHON_SAT_IMPORT_INTERVAL: "1"
    ARCHON_SAT_METRICS_PORT: "4236"
    ARCHON_SAT_NETWORK: testnet
    ARCHON_SAT_PASS: signet
    ARCHON_SAT_PORT: "38332"
    ARCHON_SAT_RBF_ENABLED: "false"
    ARCHON_SAT_REIMPORT: "true"
    ARCHON_SAT_START_BLOCK: "288000"
    ARCHON_SAT_USER: signet
    ARCHON_WALLET_URL: http://btc-signet-wallet:4240
    image: ghcr.io/archetech/satoshi-mediator
    networks:
    default: null
    ports:
    - mode: ingress
    host_ip: 127.0.0.1
    target: 4236
    published: "4236"
    protocol: tcp
    user: 1000:1000
    volumes:
    - type: bind
    source: /home/david/archetech/archon/data
    target: /app/satoshi/data
    bind:
    create_host_path: true
    btc-signet-node:
    image: ghcr.io/archetech/bitcoin-core:v28.0-multiarch
    networks:
    default: null
    ports:
    - mode: ingress
    host_ip: 127.0.0.1
    target: 38332
    published: "38332"
    protocol: tcp
    volumes:
    - type: bind
    source: /home/david/archetech/archon/data/btc-signet
    target: /root/.bitcoin
    bind:
    create_host_path: true
    btc-signet-wallet:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.wallet
    args:
    GIT_COMMIT: unknown
    depends_on:
    keymaster:
    condition: service_started
    required: true
    environment:
    ARCHON_ADMIN_API_KEY: DO3E06-un0cBMxdHXxYy8TxnjlgOlO9p87-CVJKTPj49Mw==
    ARCHON_KEYMASTER_URL: http://keymaster:4226
    ARCHON_NODE_ID: Wolverine
    ARCHON_WALLET_BTC_HOST: btc-signet-node
    ARCHON_WALLET_BTC_PASS: signet
    ARCHON_WALLET_BTC_PORT: "38332"
    ARCHON_WALLET_BTC_USER: signet
    ARCHON_WALLET_METRICS_PORT: "4241"
    ARCHON_WALLET_NETWORK: signet
    ARCHON_WALLET_PORT: "4240"
    image: ghcr.io/archetech/wallet
    networks:
    default: null
    ports:
    - mode: ingress
    host_ip: 127.0.0.1
    target: 4240
    published: "4240"
    protocol: tcp
    - mode: ingress
    host_ip: 127.0.0.1
    target: 4241
    published: "4241"
    protocol: tcp
    user: 1000:1000
    cli:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.cli
    args:
    GIT_COMMIT: unknown
    depends_on:
    gatekeeper:
    condition: service_healthy
    required: true
    ipfs:
    condition: service_healthy
    required: true
    keymaster:
    condition: service_healthy
    required: true
    environment:
    ARCHON_ADMIN_API_KEY: DO3E06-un0cBMxdHXxYy8TxnjlgOlO9p87-CVJKTPj49Mw==
    ARCHON_GATEKEEPER_URL: http://gatekeeper:4224
    ARCHON_IPFS_URL: http://ipfs:5001/api/v0
    ARCHON_KEYMASTER_URL: http://keymaster:4226
    image: ghcr.io/archetech/cli
    networks:
    default: null
    user: 1000:1000
    volumes:
    - type: bind
    source: /home/david/archetech/archon/share
    target: /app/share
    bind:
    create_host_path: true
    cln-mainnet-node:
    environment:
    ALIAS: archon:wolverine
    ANNOUNCE_ADDR: ""
    BITCOIN_RPCHOST: taranis
    BITCOIN_RPCPASSWORD: OiNHh1VH5oacjAmpIg8yTPgROoiLVBiGbVyG634gxHE=
    BITCOIN_RPCPORT: "8332"
    BITCOIN_RPCUSER: umbrel
    HIVE_ARCHON_ENABLED: "true"
    HIVE_ARCHON_VERSION: v0.1.0
    HIVE_COMMS_ENABLED: "true"
    HIVE_GOVERNANCE_MODE: advisor
    LIGHTNING_PORT: "9736"
    LOG_LEVEL: info
    MIN_CAPACITY_SAT: "50000"
    NETWORK: bitcoin
    NETWORK_MODE: tor
    RGB: e33502
    image: ghcr.io/archetech/cl-hive-node:main
    networks:
    default:
    aliases:
    - cln
    ports:
    - mode: ingress
    target: 9736
    published: "9736"
    protocol: tcp
    - mode: ingress
    host_ip: 127.0.0.1
    target: 3001
    published: "3001"
    protocol: tcp
    volumes:
    - type: bind
    source: /home/david/archetech/archon/data/cln-mainnet
    target: /data/lightning
    bind:
    create_host_path: true
    - type: bind
    source: /home/david/archetech/archon/data/cln-mainnet/tor
    target: /var/lib/tor
    bind:
    create_host_path: true
    drawbridge:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.drawbridge
    args:
    GIT_COMMIT: unknown
    depends_on:
    gatekeeper:
    condition: service_healthy
    required: true
    redis:
    condition: service_healthy
    required: true
    entrypoint:
    - /bin/sh
    - /scripts/drawbridge-entrypoint.sh
    environment:
    ARCHON_ADMIN_API_KEY: DO3E06-un0cBMxdHXxYy8TxnjlgOlO9p87-CVJKTPj49Mw==
    ARCHON_BIND_ADDRESS: 0.0.0.0
    ARCHON_DRAWBRIDGE_CLN_REST_URL: https://cln:3001
    ARCHON_DRAWBRIDGE_CLN_RUNE: ""
    ARCHON_DRAWBRIDGE_DEFAULT_PRICE_SATS: "10"
    ARCHON_DRAWBRIDGE_INVOICE_EXPIRY: "3600"
    ARCHON_DRAWBRIDGE_L402_ENABLED: "false"
    ARCHON_DRAWBRIDGE_LNBITS_URL: http://lnbits:5000
    ARCHON_DRAWBRIDGE_MACAROON_SECRET: ""
    ARCHON_DRAWBRIDGE_PORT: "4222"
    ARCHON_DRAWBRIDGE_PUBLIC_HOST: ""
    ARCHON_DRAWBRIDGE_RATE_LIMIT_MAX: "100"
    ARCHON_DRAWBRIDGE_RATE_LIMIT_WINDOW: "60"
    ARCHON_GATEKEEPER_URL: http://gatekeeper:4224
    ARCHON_REDIS_URL: redis://redis:6379
    ARCHON_TOR_PROXY: tor:9050
    healthcheck:
    test:
    - CMD
    - node
    - -e
    - const http=require('http');const req=http.get('http://127.0.0.1:4222/api/v1/ready',res=>{let body='';res.on('data',chunk=>body+=chunk);res.on('end',()=>process.exit(res.statusCode===200&&(body.trim()==='true'||body.includes('"ready":true'))?0:1));});req.on('error',()=>process.exit(1));
    timeout: 5s
    interval: 15s
    retries: 6
    start_period: 25s
    image: ghcr.io/archetech/drawbridge
    networks:
    default: null
    ports:
    - mode: ingress
    target: 4222
    published: "4222"
    protocol: tcp
    user: 1000:1000
    volumes:
    - type: bind
    source: /home/david/archetech/archon/scripts/drawbridge-entrypoint.sh
    target: /scripts/drawbridge-entrypoint.sh
    read_only: true
    bind:
    create_host_path: true
    - type: bind
    source: /home/david/archetech/archon/data/drawbridge
    target: /data/drawbridge
    bind:
    create_host_path: true
    - type: bind
    source: /home/david/archetech/archon/data/cln-mainnet
    target: /data/lightning
    read_only: true
    bind:
    create_host_path: true
    - type: volume
    source: tor-hostname
    target: /data/tor
    read_only: true
    volume: {}
    drawbridge-client:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.gatekeeper-client
    args:
    GIT_COMMIT: unknown
    VITE_SERVER_PORT: "4222"
    depends_on:
    drawbridge:
    condition: service_healthy
    required: true
    environment:
    VITE_PORT: "4223"
    healthcheck:
    test:
    - CMD
    - node
    - -e
    - const http=require('http');const req=http.get('http://127.0.0.1:4223/',res=>process.exit(res.statusCode===200?0:1));req.on('error',()=>process.exit(1));
    timeout: 5s
    interval: 15s
    retries: 6
    start_period: 20s
    image: ghcr.io/archetech/drawbridge-client
    networks:
    default: null
    ports:
    - mode: ingress
    target: 4223
    published: "4223"
    protocol: tcp
    user: 1000:1000
    drawbridge-init:
    command:
    - |
    RUNE_FILE=/data/lightning/drawbridge/rune.txt
    RPC_SOCKET=/data/lightning/bitcoin/bitcoin/lightning-rpc

      if [ -f "$$RUNE_FILE" ]; then
          echo "[drawbridge-init] Rune already exists, skipping"
          exit 0
      fi
    
      echo "[drawbridge-init] Waiting for lightningd..."
      timeout=600; elapsed=0
      while ! lightning-cli --conf=/dev/null --rpc-file="$$RPC_SOCKET" getinfo >/dev/null 2>&1; do
          sleep 2; elapsed=$$((elapsed + 2))
          if [ $$elapsed -ge $$timeout ]; then
              echo "[drawbridge-init] ERROR: lightningd not ready after $${timeout}s"
              exit 1
          fi
      done
    
      echo "[drawbridge-init] Creating rune..."
      set -e
      mkdir -p /data/lightning/drawbridge
      RUNE=$$(lightning-cli --conf=/dev/null --rpc-file="$$RPC_SOCKET" createrune \
          restrictions='[["method^invoice","method^listinvoices"]]' \
          | python3 -c "import sys,json; print(json.load(sys.stdin)['rune'])")
    
      if [ -z "$$RUNE" ]; then
          echo "[drawbridge-init] ERROR: Failed to create rune"
          exit 1
      fi
    
      echo "LIGHTNING_RUNE=\"$$RUNE\"" > "$$RUNE_FILE"
      echo "[drawbridge-init] Rune created successfully (restricted to invoice methods)"
    

    depends_on:
    cln-mainnet-node:
    condition: service_started
    required: true
    entrypoint:
    - /bin/bash
    - -c
    image: ghcr.io/archetech/cl-hive-node:main
    networks:
    default: null
    restart: "no"
    volumes:
    - type: bind
    source: /home/david/archetech/archon/data/cln-mainnet
    target: /data/lightning
    bind:
    create_host_path: true
    explorer:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.explorer
    args:
    GIT_COMMIT: unknown
    depends_on:
    gatekeeper:
    condition: service_healthy
    required: true
    environment:
    VITE_EXPLORER_PORT: "4000"
    VITE_GATEKEEPER_URL: http://localhost:4224
    VITE_OPERATION_NETWORKS: hyperswarm,local,BTC:signet,BTC:testnet4
    VITE_SEARCH_SERVER: http://localhost:4224
    healthcheck:
    test:
    - CMD
    - node
    - -e
    - const http=require('http');const req=http.get('http://127.0.0.1:4000/version',res=>process.exit(res.statusCode===200?0:1));req.on('error',()=>process.exit(1));
    timeout: 5s
    interval: 15s
    retries: 6
    start_period: 20s
    image: ghcr.io/archetech/explorer
    networks:
    default: null
    ports:
    - mode: ingress
    target: 4000
    published: "4000"
    protocol: tcp
    gatekeeper:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.gatekeeper
    args:
    GIT_COMMIT: unknown
    depends_on:
    ipfs:
    condition: service_healthy
    required: true
    mongodb:
    condition: service_healthy
    required: true
    redis:
    condition: service_healthy
    required: true
    environment:
    ARCHON_ADMIN_API_KEY: DO3E06-un0cBMxdHXxYy8TxnjlgOlO9p87-CVJKTPj49Mw==
    ARCHON_BIND_ADDRESS: 0.0.0.0
    ARCHON_GATEKEEPER_DB: redis
    ARCHON_GATEKEEPER_DID_PREFIX: did:cid
    ARCHON_GATEKEEPER_FALLBACK_TIMEOUT: "5000"
    ARCHON_GATEKEEPER_FALLBACK_URL: https://dev.uniresolver.io
    ARCHON_GATEKEEPER_GC_INTERVAL: "60"
    ARCHON_GATEKEEPER_PORT: "4224"
    ARCHON_GATEKEEPER_REGISTRIES: hyperswarm
    ARCHON_GATEKEEPER_STATUS_INTERVAL: "1"
    ARCHON_GATEKEEPER_UPLOAD_LIMIT: 100mb
    ARCHON_IPFS_URL: http://ipfs:5001/api/v0
    ARCHON_MONGODB_URL: mongodb://mongodb:27017
    ARCHON_REDIS_URL: redis://redis:6379
    healthcheck:
    test:
    - CMD
    - node
    - -e
    - const http=require('http');const req=http.get('http://127.0.0.1:4224/api/v1/ready',res=>{let body='';res.on('data',chunk=>body+=chunk);res.on('end',()=>process.exit(res.statusCode===200&&(body.trim()==='true'||body.includes('"ready":true'))?0:1));});req.on('error',()=>process.exit(1));
    timeout: 5s
    interval: 10s
    retries: 6
    start_period: 20s
    image: ghcr.io/archetech/gatekeeper
    networks:
    default: null
    ports:
    - mode: ingress
    target: 4224
    published: "4224"
    protocol: tcp
    user: 1000:1000
    volumes:
    - type: bind
    source: /home/david/archetech/archon/data
    target: /app/gatekeeper/data
    bind:
    create_host_path: true
    gatekeeper-client:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.gatekeeper-client
    args:
    GIT_COMMIT: unknown
    VITE_SERVER_PORT: "4224"
    depends_on:
    gatekeeper:
    condition: service_healthy
    required: true
    environment:
    VITE_PORT: "4225"
    healthcheck:
    test:
    - CMD
    - node
    - -e
    - const http=require('http');const req=http.get('http://127.0.0.1:4225/',res=>process.exit(res.statusCode===200?0:1));req.on('error',()=>process.exit(1));
    timeout: 5s
    interval: 15s
    retries: 6
    start_period: 20s
    image: ghcr.io/archetech/gatekeeper-client
    networks:
    default: null
    ports:
    - mode: ingress
    target: 4225
    published: "4225"
    protocol: tcp
    user: 1000:1000
    grafana:
    depends_on:
    prometheus:
    condition: service_started
    required: true
    environment:
    GF_SECURITY_ADMIN_PASSWORD: admin
    GF_SECURITY_ADMIN_USER: admin
    GF_USERS_ALLOW_SIGN_UP: "false"
    GF_USERS_DEFAULT_THEME: dark
    image: grafana/grafana:10.4.0
    networks:
    default: null
    ports:
    - mode: ingress
    target: 3000
    published: "3000"
    protocol: tcp
    user: 1000:1000
    volumes:
    - type: bind
    source: /home/david/archetech/archon/observability/grafana/provisioning
    target: /etc/grafana/provisioning
    bind:
    create_host_path: true
    - type: bind
    source: /home/david/archetech/archon/data/grafana
    target: /var/lib/grafana
    bind:
    create_host_path: true
    hyperswarm-mediator:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.hyperswarm
    args:
    GIT_COMMIT: unknown
    depends_on:
    gatekeeper:
    condition: service_healthy
    required: true
    ipfs:
    condition: service_healthy
    required: true
    keymaster:
    condition: service_healthy
    required: true
    environment:
    ARCHON_ADMIN_API_KEY: DO3E06-un0cBMxdHXxYy8TxnjlgOlO9p87-CVJKTPj49Mw==
    ARCHON_GATEKEEPER_URL: http://gatekeeper:4224
    ARCHON_HYPR_EXPORT_INTERVAL: "2"
    ARCHON_HYPR_METRICS_PORT: "4232"
    ARCHON_IPFS_URL: http://ipfs:5001/api/v0
    ARCHON_KEYMASTER_URL: http://keymaster:4226
    ARCHON_NODE_ID: Wolverine
    ARCHON_NODE_NAME: lucifer:wolverine
    ARCHON_PROTOCOL: /ARCHON/v0.2-reboot
    healthcheck:
    test:
    - CMD
    - node
    - -e
    - const http=require('http');const req=http.get('http://127.0.0.1:4232/ready',res=>{let body='';res.on('data',chunk=>body+=chunk);res.on('end',()=>process.exit(res.statusCode===200&&body.includes('"ready":true')?0:1));});req.on('error',()=>process.exit(1));
    timeout: 5s
    interval: 15s
    retries: 6
    start_period: 25s
    image: ghcr.io/archetech/hyperswarm-mediator
    networks:
    default: null
    ports:
    - mode: ingress
    host_ip: 127.0.0.1
    target: 4232
    published: "4232"
    protocol: tcp
    user: 1000:1000
    ipfs:
    image: ipfs/kubo:v0.40.1
    networks:
    default: null
    ports:
    - mode: ingress
    target: 4001
    published: "4001"
    protocol: tcp
    - mode: ingress
    target: 4001
    published: "4001"
    protocol: udp
    - mode: ingress
    host_ip: 127.0.0.1
    target: 5001
    published: "5001"
    protocol: tcp
    volumes:
    - type: bind
    source: /home/david/archetech/archon/data/ipfs
    target: /data/ipfs
    bind:
    create_host_path: true
    - type: bind
    source: /home/david/archetech/archon/share
    target: /export/share
    bind:
    create_host_path: true
    keymaster:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.keymaster
    args:
    GIT_COMMIT: unknown
    depends_on:
    gatekeeper:
    condition: service_healthy
    required: true
    mongodb:
    condition: service_healthy
    required: true
    redis:
    condition: service_healthy
    required: true
    environment:
    ARCHON_ADMIN_API_KEY: DO3E06-un0cBMxdHXxYy8TxnjlgOlO9p87-CVJKTPj49Mw==
    ARCHON_BIND_ADDRESS: 0.0.0.0
    ARCHON_DEFAULT_REGISTRY: hyperswarm
    ARCHON_DISABLE_SEARCH: "false"
    ARCHON_ENCRYPTED_PASSPHRASE: WolverineFTW!
    ARCHON_GATEKEEPER_URL: http://gatekeeper:4224
    ARCHON_KEYMASTER_DB: json
    ARCHON_KEYMASTER_PORT: "4226"
    ARCHON_KEYMASTER_UPLOAD_LIMIT: 100mb
    ARCHON_MONGODB_URL: mongodb://mongodb:27017
    ARCHON_NODE_ID: Wolverine
    ARCHON_REDIS_URL: redis://redis:6379
    ARCHON_WALLET_CACHE: "false"
    healthcheck:
    test:
    - CMD
    - node
    - -e
    - const http=require('http');const req=http.get('http://127.0.0.1:4226/api/v1/ready',res=>{let body='';res.on('data',chunk=>body+=chunk);res.on('end',()=>process.exit(res.statusCode===200&&body.includes('"ready":true')?0:1));});req.on('error',()=>process.exit(1));
    timeout: 5s
    interval: 10s
    retries: 6
    start_period: 20s
    image: ghcr.io/archetech/keymaster
    networks:
    default: null
    ports:
    - mode: ingress
    target: 4226
    published: "4226"
    protocol: tcp
    user: 1000:1000
    volumes:
    - type: bind
    source: /home/david/archetech/archon/data
    target: /app/keymaster/data
    bind:
    create_host_path: true
    keymaster-client:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.keymaster-client
    args:
    GIT_COMMIT: unknown
    VITE_SERVER_PORT: "4226"
    depends_on:
    keymaster:
    condition: service_healthy
    required: true
    environment:
    VITE_PORT: "4227"
    healthcheck:
    test:
    - CMD
    - node
    - -e
    - const http=require('http');const req=http.get('http://127.0.0.1:4227/',res=>process.exit(res.statusCode===200?0:1));req.on('error',()=>process.exit(1));
    timeout: 5s
    interval: 15s
    retries: 6
    start_period: 20s
    image: ghcr.io/archetech/keymaster-client
    networks:
    default: null
    ports:
    - mode: ingress
    target: 4227
    published: "4227"
    protocol: tcp
    user: 1000:1000
    lnbits:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.lnbits
    depends_on:
    cln-mainnet-node:
    condition: service_started
    required: true
    lnbits-init:
    condition: service_completed_successfully
    required: true
    entrypoint:
    - /bin/sh
    - /scripts/lnbits-entrypoint.sh
    environment:
    AUTH_SECRET_KEY: ""
    CLNREST_CA: /data/lightning/lnbits/ca.pem
    CLNREST_URL: https://cln:3001
    FORWARDED_ALLOW_IPS: '*'
    LNBITS_ADMIN_UI: "true"
    LNBITS_BACKEND_WALLET_CLASS: CLNRestWallet
    LNBITS_DATA_FOLDER: /data/lnbits
    LNBITS_HOST: 0.0.0.0
    LNBITS_PORT: "5000"
    image: ghcr.io/archetech/lnbits
    networks:
    default: null
    ports:
    - mode: ingress
    target: 5000
    published: "5000"
    protocol: tcp
    volumes:
    - type: bind
    source: /home/david/archetech/archon/scripts/lnbits-entrypoint.sh
    target: /scripts/lnbits-entrypoint.sh
    read_only: true
    bind:
    create_host_path: true
    - type: bind
    source: /home/david/archetech/archon/data/cln-mainnet
    target: /data/lightning
    read_only: true
    bind:
    create_host_path: true
    - type: bind
    source: /home/david/archetech/archon/data/lnbits
    target: /data/lnbits
    bind:
    create_host_path: true
    lnbits-init:
    command:
    - |
    RUNE_DIR=/data/lightning/lnbits
    RUNE_FILE=$$RUNE_DIR/runes.env
    RPC_SOCKET=/data/lightning/bitcoin/bitcoin/lightning-rpc
    CERT_DIR=/data/lightning/bitcoin/bitcoin

      if [ -f "$$RUNE_FILE" ]; then
          echo "[lnbits-init] Runes already exist, skipping"
          exit 0
      fi
    
      echo "[lnbits-init] Waiting for lightningd..."
      timeout=600; elapsed=0
      while ! lightning-cli --conf=/dev/null --rpc-file="$$RPC_SOCKET" getinfo >/dev/null 2>&1; do
          sleep 2; elapsed=$$((elapsed + 2))
          if [ $$elapsed -ge $$timeout ]; then
              echo "[lnbits-init] ERROR: lightningd not ready after $${timeout}s"
              exit 1
          fi
      done
    
      echo "[lnbits-init] Creating runes..."
      set -e
      mkdir -p "$$RUNE_DIR"
    
      READONLY=$$(lightning-cli --conf=/dev/null --rpc-file="$$RPC_SOCKET" createrune \
          restrictions='[["method=listfunds","method=listpays","method=listinvoices","method=getinfo","method=summary","method=waitanyinvoice"]]' \
          | python3 -c "import sys,json; print(json.load(sys.stdin)['rune'])")
    
      INVOICE=$$(lightning-cli --conf=/dev/null --rpc-file="$$RPC_SOCKET" createrune \
          restrictions='[["method=invoice"],["rate=120"]]' \
          | python3 -c "import sys,json; print(json.load(sys.stdin)['rune'])")
    
      PAY=$$(lightning-cli --conf=/dev/null --rpc-file="$$RPC_SOCKET" createrune \
          restrictions='[["method=pay"],["rate=30"]]' \
          | python3 -c "import sys,json; print(json.load(sys.stdin)['rune'])")
    
      echo "CLNREST_READONLY_RUNE=\"$$READONLY\"" > "$$RUNE_FILE"
      echo "CLNREST_INVOICE_RUNE=\"$$INVOICE\"" >> "$$RUNE_FILE"
      echo "CLNREST_PAY_RUNE=\"$$PAY\"" >> "$$RUNE_FILE"
    
      # Copy TLS certs so LNbits can read them
      if [ -f "$$CERT_DIR/ca.pem" ]; then
          cp "$$CERT_DIR/ca.pem" "$$RUNE_DIR/ca.pem"
          cp "$$CERT_DIR/server.pem" "$$RUNE_DIR/server.pem"
          echo "[lnbits-init] TLS certs copied"
      else
          echo "[lnbits-init] WARNING: TLS certs not found at $$CERT_DIR"
      fi
    
      echo "[lnbits-init] Runes created successfully"
    

    depends_on:
    cln-mainnet-node:
    condition: service_started
    required: true
    entrypoint:
    - /bin/bash
    - -c
    image: ghcr.io/archetech/cl-hive-node:main
    networks:
    default: null
    restart: "no"
    volumes:
    - type: bind
    source: /home/david/archetech/archon/data/cln-mainnet
    target: /data/lightning
    bind:
    create_host_path: true
    mongodb:
    healthcheck:
    test:
    - CMD
    - mongosh
    - --quiet
    - --eval
    - 'db.adminCommand({ ping: 1 })'
    timeout: 5s
    interval: 10s
    retries: 6
    start_period: 15s
    image: mongo:8.0
    networks:
    default: null
    ports:
    - mode: ingress
    host_ip: 127.0.0.1
    target: 27017
    published: "27017"
    protocol: tcp
    volumes:
    - type: bind
    source: /home/david/archetech/archon/data/mongodb
    target: /data/db
    bind:
    create_host_path: true
    prometheus:
    command:
    - --config.file=/etc/prometheus/prometheus.yml
    - --storage.tsdb.path=/prometheus
    - --storage.tsdb.retention.time=15d
    depends_on:
    gatekeeper:
    condition: service_healthy
    required: true
    keymaster:
    condition: service_healthy
    required: true
    image: prom/prometheus:v2.51.0
    networks:
    default: null
    ports:
    - mode: ingress
    host_ip: 127.0.0.1
    target: 9090
    published: "9090"
    protocol: tcp
    user: 1000:1000
    volumes:
    - type: bind
    source: /home/david/archetech/archon/observability/prometheus/prometheus.yml
    target: /etc/prometheus/prometheus.yml
    bind:
    create_host_path: true
    - type: bind
    source: /home/david/archetech/archon/data/prometheus
    target: /prometheus
    bind:
    create_host_path: true
    react-wallet:
    build:
    context: /home/david/archetech/archon
    dockerfile: Dockerfile.react-wallet
    args:
    GIT_COMMIT: unknown
    depends_on:
    gatekeeper:
    condition: service_healthy
    required: true
    environment:
    VITE_GATEKEEPER_URL: http://gatekeeper:4224
    VITE_KEYMASTER_URL: http://keymaster:4226
    VITE_PORT: "4228"
    VITE_SEARCH_SERVER: http://gatekeeper:4224
    healthcheck:
    test:
    - CMD
    - node
    - -e
    - const http=require('http');const req=http.get('http://127.0.0.1:4228/',res=>process.exit(res.statusCode===200?0:1));req.on('error',()=>process.exit(1));
    timeout: 5s
    interval: 15s
    retries: 6
    start_period: 20s
    image: ghcr.io/archetech/react-wallet
    networks:
    default: null
    ports:
    - mode: ingress
    target: 4228
    published: "4228"
    protocol: tcp
    user: 1000:1000
    redis:
    command:
    - redis-server
    - /usr/local/etc/redis/redis.conf
    healthcheck:
    test:
    - CMD
    - redis-cli
    - ping
    timeout: 5s
    interval: 10s
    retries: 6
    start_period: 10s
    image: redis:8.0.4-alpine
    networks:
    default: null
    ports:
    - mode: ingress
    host_ip: 127.0.0.1
    target: 6379
    published: "6379"
    protocol: tcp
    volumes:
    - type: bind
    source: /home/david/archetech/archon/data/redis.conf
    target: /usr/local/etc/redis/redis.conf
    bind:
    create_host_path: true
    - type: bind
    source: /home/david/archetech/archon/data/redis
    target: /data
    bind:
    create_host_path: true
    rtl:
    depends_on:
    cln-mainnet-node:
    condition: service_started
    required: true
    rtl-init:
    condition: service_completed_successfully
    required: true
    entrypoint:
    - /bin/sh
    - /scripts/rtl-entrypoint.sh
    environment:
    CLN_REST_HOST: cln
    CLN_REST_PORT: "3001"
    RTL_CONFIG_PATH: /data
    RTL_NODE_NAME: archon:wolverine
    RTL_PASSWORD: WolverineFTW!
    image: shahanafarooqui/rtl:0.15.1
    networks:
    default: null
    ports:
    - mode: ingress
    target: 3000
    published: "3002"
    protocol: tcp
    volumes:
    - type: bind
    source: /home/david/archetech/archon/scripts/rtl-entrypoint.sh
    target: /scripts/rtl-entrypoint.sh
    read_only: true
    bind:
    create_host_path: true
    - type: bind
    source: /home/david/archetech/archon/data/cln-mainnet/rtl
    target: /data
    bind:
    create_host_path: true
    rtl-init:
    command:
    - |
    RUNE_FILE=/data/lightning/rtl/rune.txt
    RPC_SOCKET=/data/lightning/bitcoin/bitcoin/lightning-rpc

      if [ -f "$$RUNE_FILE" ]; then
          echo "[rtl-init] Rune already exists, skipping"
          exit 0
      fi
    
      echo "[rtl-init] Waiting for lightningd..."
      timeout=600; elapsed=0
      while ! lightning-cli --conf=/dev/null --rpc-file="$$RPC_SOCKET" getinfo >/dev/null 2>&1; do
          sleep 2; elapsed=$$((elapsed + 2))
          if [ $$elapsed -ge $$timeout ]; then
              echo "[rtl-init] ERROR: lightningd not ready after $${timeout}s"
              exit 1
          fi
      done
    
      echo "[rtl-init] Creating rune..."
      set -e
      mkdir -p /data/lightning/rtl
      RUNE=$$(lightning-cli --conf=/dev/null --rpc-file="$$RPC_SOCKET" createrune \
          | python3 -c "import sys,json; print(json.load(sys.stdin)['rune'])")
    
      if [ -z "$$RUNE" ]; then
          echo "[rtl-init] ERROR: Failed to create rune"
          exit 1
      fi
    
      echo "LIGHTNING_RUNE=\"$$RUNE\"" > "$$RUNE_FILE"
      echo "[rtl-init] Rune created successfully"
    

    depends_on:
    cln-mainnet-node:
    condition: service_started
    required: true
    entrypoint:
    - /bin/bash
    - -c
    image: ghcr.io/archetech/cl-hive-node:main
    networks:
    default: null
    restart: "no"
    volumes:
    - type: bind
    source: /home/david/archetech/archon/data/cln-mainnet
    target: /data/lightning
    bind:
    create_host_path: true
    tor:
    depends_on:
    drawbridge:
    condition: service_healthy
    required: true
    entrypoint:
    - /bin/sh
    - -c
    - |
    pyentrypoint tor &
    while [ ! -f /var/lib/tor/hidden_service/drawbridge/hostname ]; do sleep 1; done
    cp /var/lib/tor/hidden_service/drawbridge/hostname /tor-hostname/hostname
    chmod 644 /tor-hostname/hostname
    echo '[tor] Published onion hostname'
    wait
    environment:
    DRAWBRIDGE_TOR_SERVICE_HOSTS: 4222:drawbridge:4222
    DRAWBRIDGE_TOR_SERVICE_VERSION: "3"
    TOR_SOCKS_PORT: 0.0.0.0:9050
    image: goldy/tor-hidden-service:latest
    networks:
    default: null
    ports:
    - mode: ingress
    target: 9050
    published: "9050"
    protocol: tcp
    volumes:
    - type: bind
    source: /home/david/archetech/archon/data/tor-drawbridge
    target: /var/lib/tor/hidden_service
    bind:
    create_host_path: true
    - type: volume
    source: tor-hostname
    target: /tor-hostname
    volume: {}
    networks:
    default:
    name: archon_default
    volumes:
    tor-hostname:
    name: archon_tor-hostname

Closes #248

@macterra macterra linked an issue Mar 21, 2026 that may be closed by this pull request
@macterra macterra merged commit 6008ac5 into main Mar 21, 2026
13 checks passed
@macterra macterra deleted the 248-add-docker-healthchecks branch March 21, 2026 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Docker healthchecks for core Archon services

1 participant