{
  "manifest_version": "1.0.0",
  "template": {
    "id": "931d753b-5d01-42ed-a7c3-f463ac6e8e49",
    "slug": "postgres-18-pgvector-vector-database",
    "name": "Postgres 18 + pgvector Vector Database",
    "description": "Drop-in Postgres with pgvector, pooling and backups for RAG and embeddings.",
    "url": "https://railway.com/deploy/postgres-18-pgvector-vector-database"
  },
  "services": [
    {
      "name": "Postgres",
      "source": {
        "image": "ghcr.io/railwayapp-templates/postgres-ssl:18"
      },
      "needs_volume": true,
      "volume_mount_path": "/var/lib/postgresql/data",
      "tcp_ports": [
        5432
      ],
      "http": false
    },
    {
      "name": "Postgres Backup",
      "source": {
        "repo": "https://github.com/baranberkay96/pgvector-railway"
      },
      "needs_volume": false,
      "http": false
    },
    {
      "name": "PgBouncer",
      "source": {
        "image": "edoburu/pgbouncer:v1.26.1-p0"
      },
      "needs_volume": false,
      "http": false
    }
  ],
  "required_inputs": [
    {
      "key": "PGDATA",
      "service": "Postgres",
      "description": "Data directory inside the volume (Railway base layout; the image refuses to start with anything outside /var/lib/postgresql/data).",
      "secret": false,
      "strategy": "default",
      "default": "/var/lib/postgresql/data/pgdata"
    },
    {
      "key": "PGHOST",
      "service": "Postgres",
      "description": "Private hostname of this database (postgres.railway.internal). Used by the Data panel and by the services that reference it.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "PGPORT",
      "service": "Postgres",
      "description": "Postgres port (the image always listens on 5432).",
      "secret": false,
      "strategy": "default",
      "default": "5432"
    },
    {
      "key": "PGUSER",
      "service": "Postgres",
      "description": "User for the Data panel and libpq clients.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGDATABASE",
      "service": "Postgres",
      "description": "Default database for the Data panel and libpq clients.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGPASSWORD",
      "service": "Postgres",
      "description": "Password for the Data panel and libpq clients.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "POSTGRES_DB",
      "service": "Postgres",
      "description": "Database created on first boot; the vector and pg_stat_statements extensions are created in it.",
      "secret": false,
      "strategy": "default",
      "default": "railway"
    },
    {
      "key": "DATABASE_URL",
      "service": "Postgres",
      "description": "Direct connection over the private network. Use it for migrations, index builds and session features (LISTEN/NOTIFY, advisory locks, SET).",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "PG_CPU_LIMIT",
      "service": "Postgres",
      "description": "CPUs the tuning plans for (parallel workers). auto = the container CPU quota capped at 8; set a number to override.",
      "secret": false,
      "strategy": "default",
      "default": "auto"
    },
    {
      "key": "POSTGRES_USER",
      "service": "Postgres",
      "description": "Superuser created on first boot.",
      "secret": false,
      "strategy": "default",
      "default": "postgres"
    },
    {
      "key": "SSL_CERT_DAYS",
      "service": "Postgres",
      "description": "Validity of the self-signed TLS certificate; it is renewed on a redeploy within 30 days of expiry.",
      "secret": false,
      "strategy": "default",
      "default": "820"
    },
    {
      "key": "PGVECTOR_TUNING",
      "service": "Postgres",
      "description": "auto = size memory and parallelism from the container limits at every boot (pgvector-tuning.conf); off = Postgres and Railway defaults.",
      "secret": false,
      "strategy": "default",
      "default": "auto"
    },
    {
      "key": "DATABASE_POOL_URL",
      "service": "Postgres",
      "description": "Pooled connection through PgBouncer (transaction mode, private network only). Use it for application queries.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "POSTGRES_PASSWORD",
      "service": "Postgres",
      "description": "Superuser password, generated at deploy (letters only, so it is URL-safe). Changing it later needs ALTER USER as well.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "PG_MEMORY_LIMIT_MB",
      "service": "Postgres",
      "description": "RAM the tuning plans for. auto = the container limit capped at 8192 MB; set a number (e.g. 16384) after raising the service's memory.",
      "secret": false,
      "strategy": "default",
      "default": "auto"
    },
    {
      "key": "DATABASE_PUBLIC_URL",
      "service": "Postgres",
      "description": "Connection from outside Railway through the TCP proxy (egress is billed). Add sslmode=require for psql/libpq; the certificate is self-signed.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "RAILWAY_SHM_SIZE_BYTES",
      "service": "Postgres",
      "description": "Size of /dev/shm (1 GiB; Railway default 64 MB). Parallel HNSW builds keep the graph in shared memory; only memory in use is billed.",
      "secret": false,
      "strategy": "default",
      "default": "1073741824"
    },
    {
      "key": "RAILWAY_DEPLOYMENT_DRAINING_SECONDS",
      "service": "Postgres",
      "description": "Time Postgres gets to shut down cleanly on redeploy.",
      "secret": false,
      "strategy": "default",
      "default": "60"
    },
    {
      "key": "PGHOST",
      "service": "Postgres Backup",
      "description": "Postgres private hostname (backups bypass PgBouncer).",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "PGPORT",
      "service": "Postgres Backup",
      "description": "Postgres port.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGUSER",
      "service": "Postgres Backup",
      "description": "Superuser, needed to dump roles and every database.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGAPPNAME",
      "service": "Postgres Backup",
      "description": "application_name shown in pg_stat_activity while a backup runs.",
      "secret": false,
      "strategy": "default",
      "default": "pgvector-backup"
    },
    {
      "key": "PGDATABASE",
      "service": "Postgres Backup",
      "description": "Database used to list the others.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGPASSWORD",
      "service": "Postgres Backup",
      "description": "Password of that user.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "BACKUP_PREFIX",
      "service": "Postgres Backup",
      "description": "Folder in the bucket; each run writes {prefix}/{UTC timestamp}/.",
      "secret": false,
      "strategy": "default",
      "default": "postgres"
    },
    {
      "key": "BACKUP_KEEP_MIN",
      "service": "Postgres Backup",
      "description": "Always keep at least this many complete runs, whatever their age.",
      "secret": false,
      "strategy": "default",
      "default": "3"
    },
    {
      "key": "BACKUP_S3_BUCKET",
      "service": "Postgres Backup",
      "description": "Railway Bucket that stores the backups.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "BACKUP_S3_REGION",
      "service": "Postgres Backup",
      "description": "Bucket region.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGCONNECT_TIMEOUT",
      "service": "Postgres Backup",
      "description": "Seconds before a connection attempt gives up (the job retries while Postgres starts).",
      "secret": false,
      "strategy": "default",
      "default": "10"
    },
    {
      "key": "BACKUP_COMPRESSION",
      "service": "Postgres Backup",
      "description": "pg_dump --compress value for the custom-format dumps (zstd:3, gzip:6, lz4, none).",
      "secret": false,
      "strategy": "default",
      "default": "zstd:3"
    },
    {
      "key": "BACKUP_S3_ENDPOINT",
      "service": "Postgres Backup",
      "description": "Bucket S3 endpoint.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "BACKUP_WAIT_SECONDS",
      "service": "Postgres Backup",
      "description": "How long a run waits for Postgres to accept connections before failing.",
      "secret": false,
      "strategy": "default",
      "default": "300"
    },
    {
      "key": "BACKUP_RETENTION_DAYS",
      "service": "Postgres Backup",
      "description": "Delete complete runs older than this many days.",
      "secret": false,
      "strategy": "default",
      "default": "14"
    },
    {
      "key": "BACKUP_S3_ACCESS_KEY_ID",
      "service": "Postgres Backup",
      "description": "Bucket access key.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "BACKUP_S3_FORCE_PATH_STYLE",
      "service": "Postgres Backup",
      "description": "Keep false: Railway Buckets use virtual-hosted-style URLs. true only for buckets whose Credentials tab says path-style.",
      "secret": false,
      "strategy": "default",
      "default": "false"
    },
    {
      "key": "BACKUP_S3_SECRET_ACCESS_KEY",
      "service": "Postgres Backup",
      "description": "Bucket secret key.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "DB_HOST",
      "service": "PgBouncer",
      "description": "Postgres private hostname that PgBouncer forwards to.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "DB_PORT",
      "service": "PgBouncer",
      "description": "Postgres port.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "DB_USER",
      "service": "PgBouncer",
      "description": "Postgres superuser; written to PgBouncer's userlist and used as auth_user, so other roles can log in through the pool too.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "AUTH_TYPE",
      "service": "PgBouncer",
      "description": "Client authentication; matches Postgres' SCRAM password encryption.",
      "secret": false,
      "strategy": "default",
      "default": "scram-sha-256"
    },
    {
      "key": "POOL_MODE",
      "service": "PgBouncer",
      "description": "transaction = a server connection per transaction (best multiplexing). Use session for apps that need session state.",
      "secret": false,
      "strategy": "default",
      "default": "transaction"
    },
    {
      "key": "ADMIN_USERS",
      "service": "PgBouncer",
      "description": "Users allowed to run SHOW POOLS / SHOW STATS on the pgbouncer admin database.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "DB_PASSWORD",
      "service": "PgBouncer",
      "description": "Password of that user (kept in sync by reference).",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "LISTEN_ADDR",
      "service": "PgBouncer",
      "description": "Listen on all IPv4 and IPv6 addresses (Railway's private network can be IPv6-only).",
      "secret": false,
      "strategy": "default",
      "default": "*"
    },
    {
      "key": "LISTEN_PORT",
      "service": "PgBouncer",
      "description": "PgBouncer port on the private network.",
      "secret": false,
      "strategy": "default",
      "default": "6432"
    },
    {
      "key": "MAX_CLIENT_CONN",
      "service": "PgBouncer",
      "description": "Maximum client connections to PgBouncer.",
      "secret": false,
      "strategy": "default",
      "default": "1000"
    },
    {
      "key": "DATABASE_POOL_URL",
      "service": "PgBouncer",
      "description": "Pooled connection string for your apps (also exposed on the Postgres service). No TLS: it stays on the private network.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "DEFAULT_POOL_SIZE",
      "service": "PgBouncer",
      "description": "Server connections per database/user pair; keep the total under Postgres max_connections (500 on Railway's image).",
      "secret": false,
      "strategy": "default",
      "default": "20"
    },
    {
      "key": "MAX_PREPARED_STATEMENTS",
      "service": "PgBouncer",
      "description": "Protocol-level prepared statements tracked per connection, so drivers that prepare statements work in transaction mode. 0 disables.",
      "secret": false,
      "strategy": "default",
      "default": "200"
    }
  ],
  "deploy": {
    "mcp": {
      "server": "railway",
      "tool": "deploy_template",
      "args": {
        "template_code": "postgres-18-pgvector-vector-database"
      }
    },
    "cli": "railway deploy --template postgres-18-pgvector-vector-database",
    "api": {
      "method": "POST",
      "path": "/graphql/v2",
      "body": {
        "query": "mutation templateDeploy($input: TemplateDeployV2Input!) { templateDeployV2(input: $input) { projectId workflowId } }",
        "variables": {
          "input": {
            "templateId": "931d753b-5d01-42ed-a7c3-f463ac6e8e49",
            "serializedConfig": {
              "buckets": {
                "1162ae9a-0ff5-5133-9785-c6859b0d89dd": {
                  "name": "Bucket"
                }
              },
              "services": {
                "6e48a648-df98-5283-a2f2-bb1829bb17e4": {
                  "icon": "https://devicons.railway.app/i/postgresql.svg",
                  "name": "Postgres",
                  "deploy": {
                    "startCommand": "/usr/bin/tini -g -- /bin/bash -c 'set -e; D=\"${PGDATA%/}\"; F=\"$D/pgvector-tuning.conf\"; C=\"$D/postgresql.conf\"; I=/docker-entrypoint-initdb.d; X=/tmp/pgvector-tuning.conf; echo \"CREATE EXTENSION IF NOT EXISTS vector; CREATE EXTENSION IF NOT EXISTS pg_stat_statements;\" > $I/zz-pgvector-extensions.sql; k() { v=$1; if [ $v -lt $2 ]; then v=$2; fi; if [ $v -gt $3 ]; then v=$3; fi; echo $v; }; n() { case \"$1\" in \"\"|*[!0-9]*) echo $2 ;; *) echo $1 ;; esac; }; w() { echo \"$1 = $2\" >> $X; }; if [ \"${PGVECTOR_TUNING:-auto}\" = off ]; then rm -f $F $I/zz-pgvector-conf.sh; echo \"pgvector-tune: PGVECTOR_TUNING=off, Postgres defaults apply\"; else H=$(( $(n \"$(grep -m 1 MemTotal /proc/meminfo | tr -s \" \" | cut -d \" \" -f 2)\" 1048576) / 1024 )); M=$(( $(n \"$(cat /sys/fs/cgroup/memory.max 2>/dev/null)\" $((H * 1048576))) / 1048576 )); if [ $M -gt $H ]; then M=$H; fi; B=$(k $(n \"$PG_MEMORY_LIMIT_MB\" $(k $M 256 8192)) 256 4194304); N=$(nproc); read -r Q P 2>/dev/null < /sys/fs/cgroup/cpu.max || Q=max; case \"$Q\" in \"\"|*[!0-9]*) ;; *) N=$(k $(( (Q + P - 1) / P )) 1 $N) ;; esac; N=$(k $(n \"$PG_CPU_LIMIT\" $(k $N 1 8)) 1 256); S=$(( $(n \"$(df -Pk /dev/shm 2>/dev/null | tail -n 1 | tr -s \" \" | cut -d \" \" -f 2)\" 65536) / 1024 )); SB=$(k $((B / 4)) 128 1048576); MW=$(k $((B / 4)) 64 2048); PM=$(k $((N - 1)) 0 4); PW=$(k $N 2 256); PG=$(k $((N / 2)) 0 2); FIT=$((S * 3 / 4)); if [ $FIT -lt 128 ]; then PM=0; fi; if [ $PM -gt 0 ]; then MW=$(k $MW 64 $FIT); fi; if [ $S -lt 256 ]; then PG=0; fi; echo \"# pgvector template: rewritten at every boot from budget ${B}MB (container ${M}MB), ${N} cpus, /dev/shm ${S}MB; ALTER SYSTEM overrides it\" > $X; w shared_buffers ${SB}MB; w effective_cache_size $((B * 3 / 4))MB; w maintenance_work_mem ${MW}MB; w autovacuum_work_mem $(k $((B / 16)) 64 $MW)MB; w work_mem $(k $((B / 512)) 4 64)MB; w max_worker_processes $(k $((PW + 4)) 8 512); w max_parallel_workers $PW; w max_parallel_workers_per_gather $PG; w max_parallel_maintenance_workers $PM; w random_page_cost 1.1; w effective_io_concurrency 200; w track_io_timing on; chmod 644 $X; if [ -f $D/PG_VERSION ] && [ -f $C ]; then install -o postgres -g postgres -m 600 $X $F; grep -qsF \"include_if_exists = pgvector-tuning.conf\" $C || echo \"include_if_exists = pgvector-tuning.conf\" >> $C; sync $F $C 2>/dev/null || true; else echo \"cp $X $F && chmod 600 $F && { grep -qsF pgvector-tuning.conf $C || echo include_if_exists = pgvector-tuning.conf >> $C; }\" > $I/zz-pgvector-conf.sh; fi; echo \"pgvector-tune: $(paste -sd \" \" $X)\"; fi; exec /usr/local/bin/wrapper.sh \"$@\"' pgvector-start postgres -p 5432 -c listen_addresses=*",
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "ghcr.io/railwayapp-templates/postgres-ssl:18"
                  },
                  "variables": {
                    "PGDATA": {
                      "isOptional": false,
                      "description": "Data directory inside the volume (Railway base layout; the image refuses to start with anything outside /var/lib/postgresql/data).",
                      "defaultValue": "/var/lib/postgresql/data/pgdata"
                    },
                    "PGHOST": {
                      "isOptional": false,
                      "description": "Private hostname of this database (postgres.railway.internal). Used by the Data panel and by the services that reference it.",
                      "defaultValue": "${{RAILWAY_PRIVATE_DOMAIN}}"
                    },
                    "PGPORT": {
                      "isOptional": false,
                      "description": "Postgres port (the image always listens on 5432).",
                      "defaultValue": "5432"
                    },
                    "PGUSER": {
                      "isOptional": false,
                      "description": "User for the Data panel and libpq clients.",
                      "defaultValue": "${{POSTGRES_USER}}"
                    },
                    "PGDATABASE": {
                      "isOptional": false,
                      "description": "Default database for the Data panel and libpq clients.",
                      "defaultValue": "${{POSTGRES_DB}}"
                    },
                    "PGPASSWORD": {
                      "isOptional": false,
                      "description": "Password for the Data panel and libpq clients.",
                      "defaultValue": "${{POSTGRES_PASSWORD}}"
                    },
                    "POSTGRES_DB": {
                      "isOptional": false,
                      "description": "Database created on first boot; the vector and pg_stat_statements extensions are created in it.",
                      "defaultValue": "railway"
                    },
                    "DATABASE_URL": {
                      "isOptional": false,
                      "description": "Direct connection over the private network. Use it for migrations, index builds and session features (LISTEN/NOTIFY, advisory locks, SET).",
                      "defaultValue": "postgresql://${{PGUSER}}:${{POSTGRES_PASSWORD}}@${{RAILWAY_PRIVATE_DOMAIN}}:5432/${{PGDATABASE}}"
                    },
                    "PG_CPU_LIMIT": {
                      "isOptional": true,
                      "description": "CPUs the tuning plans for (parallel workers). auto = the container CPU quota capped at 8; set a number to override.",
                      "defaultValue": "auto"
                    },
                    "POSTGRES_USER": {
                      "isOptional": false,
                      "description": "Superuser created on first boot.",
                      "defaultValue": "postgres"
                    },
                    "SSL_CERT_DAYS": {
                      "isOptional": true,
                      "description": "Validity of the self-signed TLS certificate; it is renewed on a redeploy within 30 days of expiry.",
                      "defaultValue": "820"
                    },
                    "PGVECTOR_TUNING": {
                      "isOptional": true,
                      "description": "auto = size memory and parallelism from the container limits at every boot (pgvector-tuning.conf); off = Postgres and Railway defaults.",
                      "defaultValue": "auto"
                    },
                    "DATABASE_POOL_URL": {
                      "isOptional": true,
                      "description": "Pooled connection through PgBouncer (transaction mode, private network only). Use it for application queries.",
                      "defaultValue": "${{PgBouncer.DATABASE_POOL_URL}}"
                    },
                    "POSTGRES_PASSWORD": {
                      "isOptional": false,
                      "description": "Superuser password, generated at deploy (letters only, so it is URL-safe). Changing it later needs ALTER USER as well.",
                      "defaultValue": "{{POSTGRES_PASSWORD}}"
                    },
                    "PG_MEMORY_LIMIT_MB": {
                      "isOptional": true,
                      "description": "RAM the tuning plans for. auto = the container limit capped at 8192 MB; set a number (e.g. 16384) after raising the service's memory.",
                      "defaultValue": "auto"
                    },
                    "DATABASE_PUBLIC_URL": {
                      "isOptional": false,
                      "description": "Connection from outside Railway through the TCP proxy (egress is billed). Add sslmode=require for psql/libpq; the certificate is self-signed.",
                      "defaultValue": "postgresql://${{PGUSER}}:${{POSTGRES_PASSWORD}}@${{RAILWAY_TCP_PROXY_DOMAIN}}:${{RAILWAY_TCP_PROXY_PORT}}/${{PGDATABASE}}"
                    },
                    "RAILWAY_SHM_SIZE_BYTES": {
                      "isOptional": true,
                      "description": "Size of /dev/shm (1 GiB; Railway default 64 MB). Parallel HNSW builds keep the graph in shared memory; only memory in use is billed.",
                      "defaultValue": "1073741824"
                    },
                    "RAILWAY_DEPLOYMENT_DRAINING_SECONDS": {
                      "isOptional": false,
                      "description": "Time Postgres gets to shut down cleanly on redeploy.",
                      "defaultValue": "60"
                    }
                  },
                  "networking": {
                    "tcpProxies": {
                      "5432": {}
                    },
                    "serviceDomains": {}
                  },
                  "volumeMounts": {
                    "f7743741-db5d-50a3-9081-553c10047992": {
                      "mountPath": "/var/lib/postgresql/data"
                    }
                  }
                },
                "8e7ef76b-e828-5f01-a965-2d0bf3695f65": {
                  "icon": "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/rclone.svg",
                  "name": "Postgres Backup",
                  "build": {
                    "builder": "DOCKERFILE"
                  },
                  "deploy": {
                    "cronSchedule": "15 3 * * *",
                    "restartPolicyType": "NEVER"
                  },
                  "source": {
                    "repo": "baranberkay96/pgvector-railway",
                    "branch": "main",
                    "rootDirectory": "/services/backup"
                  },
                  "variables": {
                    "PGHOST": {
                      "isOptional": false,
                      "description": "Postgres private hostname (backups bypass PgBouncer).",
                      "defaultValue": "${{Postgres.RAILWAY_PRIVATE_DOMAIN}}"
                    },
                    "PGPORT": {
                      "isOptional": false,
                      "description": "Postgres port.",
                      "defaultValue": "${{Postgres.PGPORT}}"
                    },
                    "PGUSER": {
                      "isOptional": false,
                      "description": "Superuser, needed to dump roles and every database.",
                      "defaultValue": "${{Postgres.POSTGRES_USER}}"
                    },
                    "PGAPPNAME": {
                      "isOptional": true,
                      "description": "application_name shown in pg_stat_activity while a backup runs.",
                      "defaultValue": "pgvector-backup"
                    },
                    "PGDATABASE": {
                      "isOptional": false,
                      "description": "Database used to list the others.",
                      "defaultValue": "${{Postgres.POSTGRES_DB}}"
                    },
                    "PGPASSWORD": {
                      "isOptional": false,
                      "description": "Password of that user.",
                      "defaultValue": "${{Postgres.POSTGRES_PASSWORD}}"
                    },
                    "BACKUP_PREFIX": {
                      "isOptional": true,
                      "description": "Folder in the bucket; each run writes {prefix}/{UTC timestamp}/.",
                      "defaultValue": "postgres"
                    },
                    "BACKUP_KEEP_MIN": {
                      "isOptional": true,
                      "description": "Always keep at least this many complete runs, whatever their age.",
                      "defaultValue": "3"
                    },
                    "BACKUP_DATABASES": {
                      "isOptional": true,
                      "description": "Databases to dump, comma-separated. Empty = every database that accepts connections.",
                      "defaultValue": ""
                    },
                    "BACKUP_S3_BUCKET": {
                      "isOptional": false,
                      "description": "Railway Bucket that stores the backups.",
                      "defaultValue": "${{Bucket.BUCKET}}"
                    },
                    "BACKUP_S3_REGION": {
                      "isOptional": false,
                      "description": "Bucket region.",
                      "defaultValue": "${{Bucket.REGION}}"
                    },
                    "PGCONNECT_TIMEOUT": {
                      "isOptional": true,
                      "description": "Seconds before a connection attempt gives up (the job retries while Postgres starts).",
                      "defaultValue": "10"
                    },
                    "BACKUP_COMPRESSION": {
                      "isOptional": true,
                      "description": "pg_dump --compress value for the custom-format dumps (zstd:3, gzip:6, lz4, none).",
                      "defaultValue": "zstd:3"
                    },
                    "BACKUP_S3_ENDPOINT": {
                      "isOptional": false,
                      "description": "Bucket S3 endpoint.",
                      "defaultValue": "${{Bucket.ENDPOINT}}"
                    },
                    "BACKUP_WAIT_SECONDS": {
                      "isOptional": true,
                      "description": "How long a run waits for Postgres to accept connections before failing.",
                      "defaultValue": "300"
                    },
                    "BACKUP_RETENTION_DAYS": {
                      "isOptional": true,
                      "description": "Delete complete runs older than this many days.",
                      "defaultValue": "14"
                    },
                    "BACKUP_S3_ACCESS_KEY_ID": {
                      "isOptional": false,
                      "description": "Bucket access key.",
                      "defaultValue": "${{Bucket.ACCESS_KEY_ID}}"
                    },
                    "BACKUP_S3_FORCE_PATH_STYLE": {
                      "isOptional": true,
                      "description": "Keep false: Railway Buckets use virtual-hosted-style URLs. true only for buckets whose Credentials tab says path-style.",
                      "defaultValue": "false"
                    },
                    "BACKUP_S3_SECRET_ACCESS_KEY": {
                      "isOptional": false,
                      "description": "Bucket secret key.",
                      "defaultValue": "${{Bucket.SECRET_ACCESS_KEY}}"
                    }
                  },
                  "networking": {
                    "tcpProxies": {},
                    "serviceDomains": {}
                  }
                },
                "b4f70ca0-2482-5441-aebb-e9047ccc5638": {
                  "icon": "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/postgresql.svg",
                  "name": "PgBouncer",
                  "deploy": {
                    "restartPolicyType": "ALWAYS"
                  },
                  "source": {
                    "image": "edoburu/pgbouncer:v1.26.1-p0"
                  },
                  "variables": {
                    "DB_HOST": {
                      "isOptional": false,
                      "description": "Postgres private hostname that PgBouncer forwards to.",
                      "defaultValue": "${{Postgres.RAILWAY_PRIVATE_DOMAIN}}"
                    },
                    "DB_PORT": {
                      "isOptional": false,
                      "description": "Postgres port.",
                      "defaultValue": "${{Postgres.PGPORT}}"
                    },
                    "DB_USER": {
                      "isOptional": false,
                      "description": "Postgres superuser; written to PgBouncer's userlist and used as auth_user, so other roles can log in through the pool too.",
                      "defaultValue": "${{Postgres.POSTGRES_USER}}"
                    },
                    "AUTH_TYPE": {
                      "isOptional": true,
                      "description": "Client authentication; matches Postgres' SCRAM password encryption.",
                      "defaultValue": "scram-sha-256"
                    },
                    "POOL_MODE": {
                      "isOptional": true,
                      "description": "transaction = a server connection per transaction (best multiplexing). Use session for apps that need session state.",
                      "defaultValue": "transaction"
                    },
                    "ADMIN_USERS": {
                      "isOptional": true,
                      "description": "Users allowed to run SHOW POOLS / SHOW STATS on the pgbouncer admin database.",
                      "defaultValue": "${{Postgres.POSTGRES_USER}}"
                    },
                    "DB_PASSWORD": {
                      "isOptional": false,
                      "description": "Password of that user (kept in sync by reference).",
                      "defaultValue": "${{Postgres.POSTGRES_PASSWORD}}"
                    },
                    "LISTEN_ADDR": {
                      "isOptional": true,
                      "description": "Listen on all IPv4 and IPv6 addresses (Railway's private network can be IPv6-only).",
                      "defaultValue": "*"
                    },
                    "LISTEN_PORT": {
                      "isOptional": true,
                      "description": "PgBouncer port on the private network.",
                      "defaultValue": "6432"
                    },
                    "MAX_CLIENT_CONN": {
                      "isOptional": true,
                      "description": "Maximum client connections to PgBouncer.",
                      "defaultValue": "1000"
                    },
                    "DATABASE_POOL_URL": {
                      "isOptional": true,
                      "description": "Pooled connection string for your apps (also exposed on the Postgres service). No TLS: it stays on the private network.",
                      "defaultValue": "postgresql://${{Postgres.POSTGRES_USER}}:${{Postgres.POSTGRES_PASSWORD}}@${{RAILWAY_PRIVATE_DOMAIN}}:${{LISTEN_PORT}}/${{Postgres.POSTGRES_DB}}"
                    },
                    "DEFAULT_POOL_SIZE": {
                      "isOptional": true,
                      "description": "Server connections per database/user pair; keep the total under Postgres max_connections (500 on Railway's image).",
                      "defaultValue": "20"
                    },
                    "MAX_PREPARED_STATEMENTS": {
                      "isOptional": true,
                      "description": "Protocol-level prepared statements tracked per connection, so drivers that prepare statements work in transaction mode. 0 disables.",
                      "defaultValue": "200"
                    }
                  },
                  "networking": {
                    "tcpProxies": {},
                    "serviceDomains": {}
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "post_deploy": {},
  "resources": {
    "expected_services": 3,
    "needs_volume": true
  },
  "generated_at": "2026-10-19T22:14:48.327Z",
  "generator_version": "0.1.0",
  "status": "degraded",
  "validated_at": "2026-10-19T22:04:32.067Z",
  "success_rate_30d": 0,
  "validation": {
    "last_run_id": "run_9d5cc9c25580414fa00c",
    "checks": [
      {
        "name": "workflow_completed",
        "passed": false,
        "detail": "Your workspace has been restricted. Please contact support to resolve this."
      }
    ]
  }
}
