version: "3.7" services: app_proxy: environment: APP_HOST: $APP_MEMPOOL_IP APP_PORT: $APP_MEMPOOL_PORT PROXY_AUTH_ADD: "false" web: image: mempool/frontend:v3.3.1@sha256:0a162e7e0d26a01e9686ddf69c96c4beae5fe10b0daa1020f3d392e033c058f1 user: "1000:1000" init: true restart: on-failure stop_grace_period: 1m command: "./wait-for mariadb:3306 --timeout=720 -- nginx -g 'daemon off;'" environment: FRONTEND_HTTP_PORT: $APP_MEMPOOL_PORT BACKEND_MAINNET_HTTP_HOST: $APP_MEMPOOL_API_IP NGINX_PORT: $APP_MEMPOOL_PORT NGINX_HOSTNAME: $APP_MEMPOOL_API_IP LIGHTNING_DETECTED_PORT: $APP_MEMPOOL_LIGHTNING_NODE_PORT networks: default: ipv4_address: $APP_MEMPOOL_IP api: image: mempool/backend:v3.3.1@sha256:358c0a517c8dcf26e7f5c02447de5bab33ec7e3fa6318685cf8012ce36098e3a user: "1000:1000" init: true restart: on-failure stop_grace_period: 1m # /backend/cache is ${APP_DATA_DIR}/data bind-mounted into this container, # so removing rbfcache.json here removes the persistent host-side cache file # that would otherwise survive container restarts. # # Mempool restores rbfcache.json with a whole-file read + JSON.parse before # the backend is healthy. If the file is too large, V8 can abort before # app-level error handling gets control. # # Keep this guard in the container command instead of an Umbrel pre-start # hook because Docker's restart:on-failure path reruns the container command # on every backend start. Umbrel lifecycle hooks only run when Umbrel starts # or updates the app, so they would not break a Docker-managed restart loop. command: - /bin/sh - -c - | set -eu max="$${MEMPOOL_RBF_CACHE_MAX_BYTES:-0}" case "$${max}" in ""|*[!0-9]*) max="0" ;; esac if [ "$${max}" -gt 0 ]; then for file in /backend/cache/rbfcache.json /backend/cache/tmp-rbfcache.json; do if [ -f "$${file}" ]; then size="$$(wc -c < "$${file}" | tr -d "[:space:]")" case "$${size}" in ""|*[!0-9]*) size="0" ;; esac if [ "$${size}" -gt "$${max}" ]; then rm -f "$${file}" echo "mempool startup: removed oversized $${file} ($${size} bytes > $${max} bytes)" fi fi done fi exec ./wait-for-it.sh mariadb:3306 --timeout=720 --strict -- ./start.sh volumes: - ${APP_DATA_DIR}/data:/backend/cache - ${UMBREL_ROOT}/app-data/lightning/data/lnd:/lnd:ro environment: CORE_RPC_HOST: $APP_BITCOIN_NODE_IP CORE_RPC_PORT: $APP_BITCOIN_RPC_PORT CORE_RPC_USERNAME: $APP_BITCOIN_RPC_USER CORE_RPC_PASSWORD: $APP_BITCOIN_RPC_PASS ELECTRUM_HOST: $APP_ELECTRS_NODE_IP ELECTRUM_PORT: $APP_ELECTRS_NODE_PORT ELECTRUM_TLS: "false" DATABASE_HOST: $APP_MEMPOOL_DB_IP DATABASE_PORT: "3306" DATABASE_DATABASE: "mempool" DATABASE_USERNAME: "mempool" DATABASE_PASSWORD: "mempool" MEMPOOL_HTTP_PORT: "8999" MEMPOOL_CACHE_DIR: "/backend/cache" MEMPOOL_CLEAR_PROTECTION_MINUTES: "20" MEMPOOL_INDEXING_BLOCKS_AMOUNT: "52560" MEMPOOL_STDOUT_LOG_MIN_PRIORITY: "info" MEMPOOL_RBF_CACHE_MAX_BYTES: "${APP_MEMPOOL_RBF_CACHE_MAX_BYTES:-67108864}" LIGHTNING_ENABLED: "true" LIGHTNING_BACKEND: "lnd" LIGHTNING_STATS_REFRESH_INTERVAL: 3600 LIGHTNING_GRAPH_REFRESH_INTERVAL: 3600 LND_TLS_CERT_PATH: "/lnd/tls.cert" LND_MACAROON_PATH: "/lnd/data/chain/bitcoin/$APP_BITCOIN_NETWORK/readonly.macaroon" LND_REST_API_URL: "https://$APP_MEMPOOL_LIGHTNING_NODE_IP:$APP_MEMPOOL_LIGHTNING_NODE_REST_PORT" LND_TIMEOUT: 120000 NODE_OPTIONS: "--max-old-space-size=${APP_MEMPOOL_NODE_MAX_OLD_SPACE_SIZE:-1024}" networks: default: ipv4_address: $APP_MEMPOOL_API_IP mariadb: image: mariadb:10.5.12@sha256:dfcba5641bdbfd7cbf5b07eeed707e6a3672f46823695a0d3aba2e49bbd9b1dd user: "1000:1000" restart: on-failure stop_grace_period: 1m volumes: - ${APP_DATA_DIR}/mysql/data:/var/lib/mysql environment: MYSQL_DATABASE: "mempool" MYSQL_USER: "mempool" MYSQL_PASSWORD: "mempool" MYSQL_ROOT_PASSWORD: "moneyprintergobrrr" networks: default: ipv4_address: $APP_MEMPOOL_DB_IP widget-server: image: getumbrel/umbrel-mempool-widget-server:v1.0.0@sha256:099846e613c7310deba842241477737babcdf78250fba6edf39d456712eeefc3 environment: MEMPOOL_API_URL: "http://$APP_MEMPOOL_IP:$APP_MEMPOOL_PORT/api/v1/fees/recommended" restart: on-failure