{
  "manifest_version": "1.0.0",
  "template": {
    "id": "d1c3eb45-8c3b-4ca2-ac16-9779b4684055",
    "slug": "postiz-updated-jul-26",
    "name": "Postiz [Updated Sep '26]",
    "description": "Postiz [Sep '26] (Schedule Posts to X, LinkedIn & Reddit) Self Host",
    "url": "https://railway.com/deploy/postiz-updated-jul-26",
    "upstream": {
      "repo_url": "https://github.com/shruti060701/railway-postiz"
    }
  },
  "services": [
    {
      "name": "selfless-dedication",
      "source": {
        "repo": "https://github.com/shruti060701/railway-postiz"
      },
      "needs_volume": false,
      "http": false
    },
    {
      "name": "remarkable-patience",
      "source": {
        "repo": "https://github.com/shruti060701/railway-postiz"
      },
      "needs_volume": false,
      "http": false
    },
    {
      "name": "temporal-postgres",
      "source": {
        "image": "postgres:16"
      },
      "needs_volume": true,
      "volume_mount_path": "/var/lib/postgresql/data",
      "http": false
    },
    {
      "name": "railway-postiz",
      "source": {
        "repo": "https://github.com/shruti060701/railway-postiz"
      },
      "needs_volume": false,
      "http": true
    },
    {
      "name": "Postgres",
      "source": {
        "image": "ghcr.io/railwayapp-templates/postgres-ssl:18"
      },
      "needs_volume": true,
      "volume_mount_path": "/var/lib/postgresql/data",
      "http": false
    },
    {
      "name": "temporal-ui",
      "source": {
        "image": "temporalio/ui:2.34.0"
      },
      "needs_volume": false,
      "http": false
    },
    {
      "name": "Redis",
      "source": {
        "image": "redis:8.2.1"
      },
      "needs_volume": true,
      "volume_mount_path": "/data",
      "http": false
    },
    {
      "name": "temporal-admin-tools",
      "source": {
        "image": "temporalio/admin-tools:1.28.1-tctl-1.18.4-cli-1.4.1"
      },
      "needs_volume": false,
      "http": false
    }
  ],
  "required_inputs": [
    {
      "key": "ES_JAVA_OPTS",
      "service": "selfless-dedication",
      "description": "Caps JVM heap size. Matches the official reference exactly — don't increase without reason, Elasticsearch here only serves Temporal's internal visibility queries, not real search traffic.",
      "secret": false,
      "strategy": "default",
      "default": "-Xms256m -Xmx256m"
    },
    {
      "key": "discovery.type",
      "service": "selfless-dedication",
      "description": "**Required — confirmed via a real crash, not a preemptive guess.** Without this, Elasticsearch refuses to start with `\"the default discovery settings are unsuitable for production use...\"`, since it assumes it's joining a multi-node production cluster. Runs as a single-node cluster here, appropriate for this use case (Temporal's internal visibility store), not a real multi-node search deployment.",
      "secret": false,
      "strategy": "default",
      "default": "single-node"
    },
    {
      "key": "xpack.security.enabled",
      "service": "selfless-dedication",
      "description": "Disables Elasticsearch's own auth layer — acceptable since this service has no public port and is only reachable from `remarkable-patience` over Railway's private network.",
      "secret": false,
      "strategy": "default",
      "default": "false"
    },
    {
      "key": "cluster.routing.allocation.disk.watermark.low",
      "service": "selfless-dedication",
      "description": "Disk watermark thresholds, matches official reference defaults.",
      "secret": false,
      "strategy": "default",
      "default": "512mb"
    },
    {
      "key": "cluster.routing.allocation.disk.watermark.high",
      "service": "selfless-dedication",
      "description": "Disk watermark threshold.",
      "secret": false,
      "strategy": "default",
      "default": "256mb"
    },
    {
      "key": "cluster.routing.allocation.disk.threshold_enabled",
      "service": "selfless-dedication",
      "description": "Enables disk-based shard allocation limits.",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "cluster.routing.allocation.disk.watermark.flood_stage",
      "service": "selfless-dedication",
      "description": "Disk watermark threshold.",
      "secret": false,
      "strategy": "default",
      "default": "128mb"
    },
    {
      "key": "DB",
      "service": "remarkable-patience",
      "description": "Tells Temporal's auto-setup which DB driver dialect to use.",
      "secret": false,
      "strategy": "default",
      "default": "postgres12"
    },
    {
      "key": "DB_PORT",
      "service": "remarkable-patience",
      "description": "Port for `temporal-postgres`.",
      "secret": false,
      "strategy": "default",
      "default": "5432"
    },
    {
      "key": "ES_SEEDS",
      "service": "remarkable-patience",
      "description": "Internal hostname of the Elasticsearch service. **Use the real service name `selfless-dedication`, not a generic `elasticsearch` placeholder.**",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "ENABLE_ES",
      "service": "remarkable-patience",
      "description": "Enables the Elasticsearch-backed visibility store.",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "ES_VERSION",
      "service": "remarkable-patience",
      "description": "Matches the pinned `elasticsearch:7.17.27` image.",
      "secret": false,
      "strategy": "default",
      "default": "v7"
    },
    {
      "key": "POSTGRES_PWD",
      "service": "remarkable-patience",
      "description": "Cross-service reference — same reasoning.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "POSTGRES_USER",
      "service": "remarkable-patience",
      "description": "Cross-service reference — must match `temporal-postgres` exactly.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "POSTGRES_SEEDS",
      "service": "remarkable-patience",
      "description": "Internal hostname of Temporal's own Postgres.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "TEMPORAL_NAMESPACE",
      "service": "remarkable-patience",
      "description": "Default Temporal namespace.",
      "secret": false,
      "strategy": "default",
      "default": "default"
    },
    {
      "key": "DYNAMIC_CONFIG_FILE_PATH",
      "service": "remarkable-patience",
      "description": "Path (relative to the image's own working directory, `/etc/temporal`) to the config file baked into the custom Dockerfile.",
      "secret": false,
      "strategy": "default",
      "default": "config/dynamicconfig/development-sql.yaml"
    },
    {
      "key": "PGDATA",
      "service": "temporal-postgres",
      "description": "**Required, confirmed via a real crash, not a preemptive guess.** Mounting the volume directly at `/var/lib/postgresql/data` leaves a `lost+found` directory there (standard on a freshly formatted volume filesystem), which breaks Postgres's own `initdb` — it expects a genuinely empty directory. Setting `PGDATA` to a subdirectory of the mount point fixes it, same pattern Railway's own managed Postgres plugin already uses everywhere else in this project.",
      "secret": false,
      "strategy": "default",
      "default": "/var/lib/postgresql/data/pgdata"
    },
    {
      "key": "POSTGRES_USER",
      "service": "temporal-postgres",
      "description": "Matches what `remarkable-patience`'s own `POSTGRES_USER` variable must reference.",
      "secret": false,
      "strategy": "default",
      "default": "temporal"
    },
    {
      "key": "POSTGRES_PASSWORD",
      "service": "temporal-postgres",
      "description": "Auto-generated. Must match what's referenced on `remarkable-patience` — set as a cross-service reference there, not independently generated (same class of desync bug already caught on this project's Typebot template).",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "PORT",
      "service": "railway-postiz",
      "description": "**Must be set as an explicit Railway variable, not just a Dockerfile default** — Railway's own edge routing needs this visible at the platform level to know where to send traffic. Confirmed the hard way on this project's Metabase template: a Dockerfile-only `ENV PORT` is invisible to Railway's routing and causes every request to hit Railway's own fallback response instead of the app.",
      "secret": false,
      "strategy": "default",
      "default": "5000"
    },
    {
      "key": "MAIN_URL",
      "service": "railway-postiz",
      "description": "Public URL of the Postiz instance.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "REDIS_URL",
      "service": "railway-postiz",
      "description": "Required (not optional, unlike some other templates' Redis) — Postiz uses Redis for queueing and caching core functionality.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "JWT_SECRET",
      "service": "railway-postiz",
      "description": "igns authentication tokens. Auto-generated per deployment.",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "DATABASE_URL",
      "service": "railway-postiz",
      "description": "Postiz's own metadata database (posts, integrations, users). Uses a standard `postgres://` URL — no special format quirk here, unlike some other templates in this project.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "FRONTEND_URL",
      "service": "railway-postiz",
      "description": "Same as `MAIN_URL` — used by the frontend build.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "TEMPORAL_ADDRESS",
      "service": "railway-postiz",
      "description": "Address of the Temporal server. Required — Postiz v2.12+ cannot run without Temporal reachable. **Use the real service name `remarkable-patience` here, not a generic `temporal` placeholder.**",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "NEXT_PUBLIC_BACKEND_URL",
      "service": "railway-postiz",
      "description": "Public API base URL the frontend calls.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "NEXT_PUBLIC_UPLOAD_DIRECTORY",
      "service": "railway-postiz",
      "description": "Public-facing path for uploaded media, matches `UPLOAD_DIRECTORY` (already baked into the Dockerfile).",
      "secret": false,
      "strategy": "default",
      "default": "/uploads"
    },
    {
      "key": "PGDATA",
      "service": "Postgres",
      "description": "Directory where Postgres stores its data files.",
      "secret": false,
      "strategy": "default",
      "default": "/var/lib/postgresql/data/pgdata"
    },
    {
      "key": "PGHOST",
      "service": "Postgres",
      "description": "Internal hostname.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "PGPORT",
      "service": "Postgres",
      "description": "Port Postgres listens on internally. Verify this is actually filled in, not left as an empty \"to be filled by the user\" placeholder — this exact composer glitch has recurred on this project's Umami/NocoDB/Metabase templates.",
      "secret": false,
      "strategy": "default",
      "default": "5432"
    },
    {
      "key": "PGUSER",
      "service": "Postgres",
      "description": "Database username.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGDATABASE",
      "service": "Postgres",
      "description": "Database name.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGPASSWORD",
      "service": "Postgres",
      "description": "Database password",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "POSTGRES_DB",
      "service": "Postgres",
      "description": "Default database name created on startup.",
      "secret": false,
      "strategy": "default",
      "default": "railway"
    },
    {
      "key": "DATABASE_URL",
      "service": "Postgres",
      "description": "Postiz's primary connection string, referenced by `railway-postiz`.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "POSTGRES_USER",
      "service": "Postgres",
      "description": "Username for the Postgres superuser account.",
      "secret": false,
      "strategy": "default",
      "default": "postgres"
    },
    {
      "key": "SSL_CERT_DAYS",
      "service": "Postgres",
      "description": "SSL certificate validity period.",
      "secret": false,
      "strategy": "default",
      "default": "820"
    },
    {
      "key": "POSTGRES_PASSWORD",
      "service": "Postgres",
      "description": "Auto-generated superuser password.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "DATABASE_PUBLIC_URL",
      "service": "Postgres",
      "description": "Public/external connection string for reaching this database from outside Railway's network.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "RAILWAY_DEPLOYMENT_DRAINING_SECONDS",
      "service": "Postgres",
      "description": "Seconds Railway waits for active connections before a redeploy. Verify this is actually filled in, same empty-placeholder caveat as `PGPORT`.",
      "secret": false,
      "strategy": "default",
      "default": "60"
    },
    {
      "key": "TEMPORAL_ADDRESS",
      "service": "temporal-ui",
      "description": "Points at the real Temporal server service name.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "REDISHOST",
      "service": "Redis",
      "description": "Internal hostname.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "REDISPORT",
      "service": "Redis",
      "description": "Port Redis listens on internally.",
      "secret": false,
      "strategy": "default",
      "default": "6379"
    },
    {
      "key": "REDISUSER",
      "service": "Redis",
      "description": "Default Redis username.",
      "secret": false,
      "strategy": "default",
      "default": "default"
    },
    {
      "key": "REDIS_URL",
      "service": "Redis",
      "description": "Primary connection string, referenced by `railway-postiz`.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "REDISPASSWORD",
      "service": "Redis",
      "description": "Redis auth password.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "REDIS_PASSWORD",
      "service": "Redis",
      "description": "Duplicate of `REDISPASSWORD` under a different variable name.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "REDIS_PUBLIC_URL",
      "service": "Redis",
      "description": "Public/external connection string for reaching Redis from outside Railway's network.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    }
  ],
  "deploy": {
    "mcp": {
      "server": "railway",
      "tool": "deploy_template",
      "args": {
        "template_code": "postiz-updated-jul-26"
      }
    },
    "cli": "railway deploy --template postiz-updated-jul-26",
    "api": {
      "method": "POST",
      "path": "/graphql/v2",
      "body": {
        "query": "mutation templateDeploy($input: TemplateDeployV2Input!) { templateDeployV2(input: $input) { projectId workflowId } }",
        "variables": {
          "input": {
            "templateId": "d1c3eb45-8c3b-4ca2-ac16-9779b4684055",
            "serializedConfig": {
              "buckets": {},
              "services": {
                "2df8b7e0-c718-4eb3-a683-b6a9789f9ff6": {
                  "icon": "https://cdn.worldvectorlogo.com/logos/elasticsearch.svg",
                  "name": "selfless-dedication",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "repo": "https://github.com/shruti060701/railway-postiz",
                    "rootDirectory": null
                  },
                  "variables": {
                    "ES_JAVA_OPTS": {
                      "isOptional": false,
                      "description": "Caps JVM heap size. Matches the official reference exactly — don't increase without reason, Elasticsearch here only serves Temporal's internal visibility queries, not real search traffic.",
                      "defaultValue": "-Xms256m -Xmx256m"
                    },
                    "discovery.type": {
                      "isOptional": false,
                      "description": "**Required — confirmed via a real crash, not a preemptive guess.** Without this, Elasticsearch refuses to start with `\"the default discovery settings are unsuitable for production use...\"`, since it assumes it's joining a multi-node production cluster. Runs as a single-node cluster here, appropriate for this use case (Temporal's internal visibility store), not a real multi-node search deployment.",
                      "defaultValue": "single-node"
                    },
                    "xpack.security.enabled": {
                      "isOptional": false,
                      "description": "Disables Elasticsearch's own auth layer — acceptable since this service has no public port and is only reachable from `remarkable-patience` over Railway's private network.",
                      "defaultValue": "false"
                    },
                    "cluster.routing.allocation.disk.watermark.low": {
                      "isOptional": true,
                      "description": "Disk watermark thresholds, matches official reference defaults.",
                      "defaultValue": "512mb"
                    },
                    "cluster.routing.allocation.disk.watermark.high": {
                      "isOptional": true,
                      "description": "Disk watermark threshold.",
                      "defaultValue": "256mb"
                    },
                    "cluster.routing.allocation.disk.threshold_enabled": {
                      "isOptional": true,
                      "description": "Enables disk-based shard allocation limits.",
                      "defaultValue": "true"
                    },
                    "cluster.routing.allocation.disk.watermark.flood_stage": {
                      "isOptional": true,
                      "description": "Disk watermark threshold.",
                      "defaultValue": "128mb"
                    }
                  }
                },
                "3ef75c18-0965-4ed5-b893-65bd40d9a65e": {
                  "icon": "https://temporal.io/favicon.svg",
                  "name": "remarkable-patience",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "repo": "https://github.com/shruti060701/railway-postiz",
                    "rootDirectory": null
                  },
                  "variables": {
                    "DB": {
                      "isOptional": false,
                      "description": "Tells Temporal's auto-setup which DB driver dialect to use.",
                      "defaultValue": "postgres12"
                    },
                    "DB_PORT": {
                      "isOptional": false,
                      "description": "Port for `temporal-postgres`.",
                      "defaultValue": "5432"
                    },
                    "ES_SEEDS": {
                      "isOptional": false,
                      "description": "Internal hostname of the Elasticsearch service. **Use the real service name `selfless-dedication`, not a generic `elasticsearch` placeholder.**",
                      "defaultValue": "${{selfless-dedication.RAILWAY_PRIVATE_DOMAIN}}"
                    },
                    "ENABLE_ES": {
                      "isOptional": false,
                      "description": "Enables the Elasticsearch-backed visibility store.",
                      "defaultValue": "true"
                    },
                    "ES_VERSION": {
                      "isOptional": false,
                      "description": "Matches the pinned `elasticsearch:7.17.27` image.",
                      "defaultValue": "v7"
                    },
                    "POSTGRES_PWD": {
                      "isOptional": false,
                      "description": "Cross-service reference — same reasoning.",
                      "defaultValue": "${{temporal-postgres.POSTGRES_PASSWORD}}"
                    },
                    "POSTGRES_USER": {
                      "isOptional": false,
                      "description": "Cross-service reference — must match `temporal-postgres` exactly.",
                      "defaultValue": "${{temporal-postgres.POSTGRES_USER}}"
                    },
                    "POSTGRES_SEEDS": {
                      "isOptional": false,
                      "description": "Internal hostname of Temporal's own Postgres.",
                      "defaultValue": "${{temporal-postgres.RAILWAY_PRIVATE_DOMAIN}}"
                    },
                    "TEMPORAL_NAMESPACE": {
                      "isOptional": false,
                      "description": "Default Temporal namespace.",
                      "defaultValue": "default"
                    },
                    "DYNAMIC_CONFIG_FILE_PATH": {
                      "isOptional": false,
                      "description": "Path (relative to the image's own working directory, `/etc/temporal`) to the config file baked into the custom Dockerfile.",
                      "defaultValue": "config/dynamicconfig/development-sql.yaml"
                    }
                  }
                },
                "49d65eec-f7cf-428d-8384-9daf5a046c03": {
                  "icon": "https://devicons.railway.app/i/postgresql.svg",
                  "name": "temporal-postgres",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "postgres:16"
                  },
                  "variables": {
                    "PGDATA": {
                      "isOptional": true,
                      "description": "**Required, confirmed via a real crash, not a preemptive guess.** Mounting the volume directly at `/var/lib/postgresql/data` leaves a `lost+found` directory there (standard on a freshly formatted volume filesystem), which breaks Postgres's own `initdb` — it expects a genuinely empty directory. Setting `PGDATA` to a subdirectory of the mount point fixes it, same pattern Railway's own managed Postgres plugin already uses everywhere else in this project.",
                      "defaultValue": "/var/lib/postgresql/data/pgdata"
                    },
                    "POSTGRES_USER": {
                      "isOptional": false,
                      "description": "Matches what `remarkable-patience`'s own `POSTGRES_USER` variable must reference.",
                      "defaultValue": "temporal"
                    },
                    "POSTGRES_PASSWORD": {
                      "isOptional": false,
                      "description": "Auto-generated. Must match what's referenced on `remarkable-patience` — set as a cross-service reference there, not independently generated (same class of desync bug already caught on this project's Typebot template).",
                      "defaultValue": "{{POSTGRES_PASSWORD}}"
                    }
                  },
                  "volumeMounts": {
                    "49d65eec-f7cf-428d-8384-9daf5a046c03": {
                      "mountPath": "/var/lib/postgresql/data"
                    }
                  }
                },
                "78b1b1bc-8216-4d1e-b2dc-39f0cafd9f20": {
                  "icon": "https://res.cloudinary.com/dh2nt6hgh/image/upload/v1760966944/postiz_logo_gza6me.png",
                  "name": "railway-postiz",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": "/",
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "repo": "https://github.com/shruti060701/railway-postiz",
                    "rootDirectory": null
                  },
                  "variables": {
                    "PORT": {
                      "isOptional": false,
                      "description": "**Must be set as an explicit Railway variable, not just a Dockerfile default** — Railway's own edge routing needs this visible at the platform level to know where to send traffic. Confirmed the hard way on this project's Metabase template: a Dockerfile-only `ENV PORT` is invisible to Railway's routing and causes every request to hit Railway's own fallback response instead of the app.",
                      "defaultValue": "5000"
                    },
                    "MAIN_URL": {
                      "isOptional": false,
                      "description": "Public URL of the Postiz instance.",
                      "defaultValue": "https://${{RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "REDIS_URL": {
                      "isOptional": false,
                      "description": "Required (not optional, unlike some other templates' Redis) — Postiz uses Redis for queueing and caching core functionality.",
                      "defaultValue": "${{Redis.REDIS_URL}}"
                    },
                    "JWT_SECRET": {
                      "isOptional": false,
                      "description": "igns authentication tokens. Auto-generated per deployment.",
                      "defaultValue": "{{JWT_SECRET}}"
                    },
                    "DATABASE_URL": {
                      "isOptional": false,
                      "description": "Postiz's own metadata database (posts, integrations, users). Uses a standard `postgres://` URL — no special format quirk here, unlike some other templates in this project.",
                      "defaultValue": "${{Postgres.DATABASE_URL}}"
                    },
                    "FRONTEND_URL": {
                      "isOptional": false,
                      "description": "Same as `MAIN_URL` — used by the frontend build.",
                      "defaultValue": "https://${{RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "TEMPORAL_ADDRESS": {
                      "isOptional": false,
                      "description": "Address of the Temporal server. Required — Postiz v2.12+ cannot run without Temporal reachable. **Use the real service name `remarkable-patience` here, not a generic `temporal` placeholder.**",
                      "defaultValue": "${{remarkable-patience.RAILWAY_PRIVATE_DOMAIN}}:7233"
                    },
                    "NEXT_PUBLIC_BACKEND_URL": {
                      "isOptional": false,
                      "description": "Public API base URL the frontend calls.",
                      "defaultValue": "https://${{RAILWAY_PUBLIC_DOMAIN}}/api"
                    },
                    "NEXT_PUBLIC_UPLOAD_DIRECTORY": {
                      "isOptional": false,
                      "description": "Public-facing path for uploaded media, matches `UPLOAD_DIRECTORY` (already baked into the Dockerfile).",
                      "defaultValue": "/uploads"
                    }
                  },
                  "networking": {
                    "serviceDomains": {
                      "<hasDomain>": {}
                    }
                  }
                },
                "a8f11865-d9da-4f9f-b9b6-70d5df0eeca8": {
                  "icon": "https://devicons.railway.app/i/postgresql.svg",
                  "name": "Postgres",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "ghcr.io/railwayapp-templates/postgres-ssl:18"
                  },
                  "variables": {
                    "PGDATA": {
                      "isOptional": true,
                      "description": "Directory where Postgres stores its data files.",
                      "defaultValue": "/var/lib/postgresql/data/pgdata"
                    },
                    "PGHOST": {
                      "isOptional": false,
                      "description": "Internal hostname.",
                      "defaultValue": "${{RAILWAY_PRIVATE_DOMAIN}}"
                    },
                    "PGPORT": {
                      "isOptional": false,
                      "description": "Port Postgres listens on internally. Verify this is actually filled in, not left as an empty \"to be filled by the user\" placeholder — this exact composer glitch has recurred on this project's Umami/NocoDB/Metabase templates.",
                      "defaultValue": "5432"
                    },
                    "PGUSER": {
                      "isOptional": false,
                      "description": "Database username.",
                      "defaultValue": "${{POSTGRES_USER}}"
                    },
                    "PGDATABASE": {
                      "isOptional": false,
                      "description": "Database name.",
                      "defaultValue": "${{POSTGRES_DB}}"
                    },
                    "PGPASSWORD": {
                      "isOptional": false,
                      "description": "Database password",
                      "defaultValue": "${{POSTGRES_PASSWORD}}"
                    },
                    "POSTGRES_DB": {
                      "isOptional": true,
                      "description": "Default database name created on startup.",
                      "defaultValue": "railway"
                    },
                    "DATABASE_URL": {
                      "isOptional": false,
                      "description": "Postiz's primary connection string, referenced by `railway-postiz`.",
                      "defaultValue": "postgresql://${{PGUSER}}:${{POSTGRES_PASSWORD}}@${{RAILWAY_PRIVATE_DOMAIN}}:5432/${{PGDATABASE}}"
                    },
                    "POSTGRES_USER": {
                      "isOptional": true,
                      "description": "Username for the Postgres superuser account.",
                      "defaultValue": "postgres"
                    },
                    "SSL_CERT_DAYS": {
                      "isOptional": true,
                      "description": "SSL certificate validity period.",
                      "defaultValue": "820"
                    },
                    "POSTGRES_PASSWORD": {
                      "isOptional": false,
                      "description": "Auto-generated superuser password.",
                      "defaultValue": "{{POSTGRES_PASSWORD}}"
                    },
                    "DATABASE_PUBLIC_URL": {
                      "isOptional": false,
                      "description": "Public/external connection string for reaching this database from outside Railway's network.",
                      "defaultValue": "postgresql://${{PGUSER}}:${{POSTGRES_PASSWORD}}@${{RAILWAY_TCP_PROXY_DOMAIN}}:${{RAILWAY_TCP_PROXY_PORT}}/${{PGDATABASE}}"
                    },
                    "RAILWAY_DEPLOYMENT_DRAINING_SECONDS": {
                      "isOptional": true,
                      "description": "Seconds Railway waits for active connections before a redeploy. Verify this is actually filled in, same empty-placeholder caveat as `PGPORT`.",
                      "defaultValue": "60"
                    }
                  },
                  "volumeMounts": {
                    "a8f11865-d9da-4f9f-b9b6-70d5df0eeca8": {
                      "mountPath": "/var/lib/postgresql/data"
                    }
                  },
                  "haTemplateCode": "postgres-ha",
                  "haConversionConfig": {
                    "edge": {
                      "label": "Reverse Proxy",
                      "options": [
                        2,
                        3,
                        4,
                        5
                      ],
                      "nodeLabel": "HAProxy",
                      "description": "Routes traffic to the primary",
                      "defaultValue": 3
                    },
                    "replica": {
                      "label": "Replicas",
                      "options": [
                        2,
                        3,
                        4,
                        5,
                        6,
                        7
                      ],
                      "nodeLabel": "Postgres",
                      "description": "Streaming replicas for automatic failover",
                      "defaultValue": 2
                    },
                    "internal": {
                      "label": "Coordinator Nodes",
                      "options": [
                        3,
                        5,
                        7,
                        9
                      ],
                      "nodeLabel": "etcd",
                      "defaultValue": 3
                    },
                    "description": "This will convert your Postgres database to an HA cluster with Patroni failover and etcd consensus."
                  }
                },
                "d1aad780-f0a7-488e-bf49-c8df67978d61": {
                  "icon": "https://temporal.io/favicon.svg",
                  "name": "temporal-ui",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "temporalio/ui:2.34.0"
                  },
                  "variables": {
                    "TEMPORAL_ADDRESS": {
                      "isOptional": false,
                      "description": "Points at the real Temporal server service name.",
                      "defaultValue": "${{remarkable-patience.RAILWAY_PRIVATE_DOMAIN}}:7233"
                    }
                  }
                },
                "db0dd5b2-f3f1-4ef8-ba85-8bb07187c387": {
                  "icon": "https://cdn.sanity.io/images/sy1jschh/production/0ce0bfdcfbdbf69662b1116671f97c2dd788b655-157x157.svg",
                  "name": "Redis",
                  "deploy": {
                    "startCommand": "/bin/sh -c \"rm -rf $RAILWAY_VOLUME_MOUNT_PATH/lost+found/ && exec docker-entrypoint.sh redis-server --requirepass $REDIS_PASSWORD --save 60 1 --dir $RAILWAY_VOLUME_MOUNT_PATH\"",
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "redis:8.2.1"
                  },
                  "variables": {
                    "REDISHOST": {
                      "isOptional": false,
                      "description": "Internal hostname.",
                      "defaultValue": "${{RAILWAY_PRIVATE_DOMAIN}}"
                    },
                    "REDISPORT": {
                      "isOptional": false,
                      "description": "Port Redis listens on internally.",
                      "defaultValue": "6379"
                    },
                    "REDISUSER": {
                      "isOptional": false,
                      "description": "Default Redis username.",
                      "defaultValue": "default"
                    },
                    "REDIS_URL": {
                      "isOptional": false,
                      "description": "Primary connection string, referenced by `railway-postiz`.",
                      "defaultValue": "redis://${{REDISUSER}}:${{REDIS_PASSWORD}}@${{REDISHOST}}:${{REDISPORT}}"
                    },
                    "REDISPASSWORD": {
                      "isOptional": false,
                      "description": "Redis auth password.",
                      "defaultValue": "${{REDIS_PASSWORD}}"
                    },
                    "REDIS_PASSWORD": {
                      "isOptional": false,
                      "description": "Duplicate of `REDISPASSWORD` under a different variable name.",
                      "defaultValue": "{{REDIS_PASSWORD}}"
                    },
                    "REDIS_PUBLIC_URL": {
                      "isOptional": false,
                      "description": "Public/external connection string for reaching Redis from outside Railway's network.",
                      "defaultValue": "redis://default:${{REDIS_PASSWORD}}@${{RAILWAY_TCP_PROXY_DOMAIN}}:${{RAILWAY_TCP_PROXY_PORT}}"
                    }
                  },
                  "volumeMounts": {
                    "db0dd5b2-f3f1-4ef8-ba85-8bb07187c387": {
                      "mountPath": "/data"
                    }
                  },
                  "haTemplateCode": "redis-ha",
                  "haConversionConfig": {
                    "edge": {
                      "label": "Reverse Proxies",
                      "options": [
                        1,
                        2
                      ],
                      "nodeLabel": "HAProxy",
                      "description": "HAProxy instances routing writes to the primary",
                      "defaultValue": 2
                    },
                    "replica": {
                      "label": "Redis Replicas",
                      "options": [
                        2,
                        3,
                        4,
                        5
                      ],
                      "nodeLabel": "Redis",
                      "description": "Nodes running Redis + colocated Sentinel",
                      "defaultValue": 2
                    },
                    "internal": null,
                    "description": "This will convert your Redis instance to an HA cluster with Sentinel failover and HAProxy routing to the primary."
                  }
                },
                "fc33782a-46f7-49af-bd06-4c09144b9bca": {
                  "icon": "https://temporal.io/favicon.svg",
                  "name": "temporal-admin-tools",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "temporalio/admin-tools:1.28.1-tctl-1.18.4-cli-1.4.1"
                  },
                  "variables": {}
                }
              }
            }
          }
        }
      }
    }
  },
  "post_deploy": {
    "healthcheck": {
      "service": "railway-postiz",
      "method": "GET",
      "path": "/",
      "expect_status": 200
    }
  },
  "resources": {
    "expected_services": 8,
    "needs_volume": true
  },
  "generated_at": "2026-09-22T22:14:50.114Z",
  "generator_version": "0.1.0",
  "status": "degraded",
  "validated_at": "2026-09-22T13:03:01.722Z",
  "success_rate_30d": 0,
  "validation": {
    "last_run_id": "run_011f830dcc0c46a49e03",
    "checks": [
      {
        "name": "workflow_completed",
        "passed": true
      },
      {
        "name": "all_services_deployed",
        "passed": false,
        "detail": "QUEUED,CRASHED,DEPLOYING,SUCCESS,SUCCESS,QUEUED,SUCCESS,SUCCESS"
      }
    ]
  }
}
