{
  "manifest_version": "1.0.0",
  "template": {
    "id": "6f8090bd-c884-4d5d-ae47-0ba2e78cd2c7",
    "slug": "infisical-secrets-platform",
    "name": "Infisical Secrets Platform",
    "description": "Self-hosted Infisical secrets platform with Postgres and Redis included",
    "url": "https://railway.com/deploy/infisical-secrets-platform",
    "upstream": {
      "image": "infisical/infisical:v0.165.13"
    }
  },
  "services": [
    {
      "name": "Infisical",
      "source": {
        "image": "infisical/infisical:v0.165.13"
      },
      "needs_volume": false,
      "http": true
    },
    {
      "name": "Postgres",
      "source": {
        "image": "ghcr.io/railwayapp-templates/postgres-ssl:17"
      },
      "needs_volume": true,
      "volume_mount_path": "/var/lib/postgresql/data",
      "tcp_ports": [
        5432
      ],
      "http": false
    },
    {
      "name": "Redis",
      "source": {
        "image": "redis:7.2.4-alpine3.19"
      },
      "needs_volume": true,
      "volume_mount_path": "/data",
      "http": false
    }
  ],
  "required_inputs": [
    {
      "key": "HOST",
      "service": "Infisical",
      "description": "Bind address. Infisical defaults to localhost, which would make the Railway healthcheck fail. Keep 0.0.0.0 so the public domain works. Reaching this service from another Railway service should use the public domain, not *.railway.internal (see README).",
      "secret": false,
      "strategy": "default",
      "default": "0.0.0.0"
    },
    {
      "key": "PORT",
      "service": "Infisical",
      "description": "Listen port. Railway's healthcheck and edge proxy probe $PORT, so keep it equal to the port Infisical binds (the server reads PORT, upstream default 8080).",
      "secret": false,
      "strategy": "default",
      "default": "8080"
    },
    {
      "key": "NODE_ENV",
      "service": "Infisical",
      "description": "Production mode. Anything else skips the boot-time Postgres/Redis connectivity check and changes cookie/security defaults.",
      "secret": false,
      "strategy": "default",
      "default": "production"
    },
    {
      "key": "SITE_URL",
      "service": "Infisical",
      "description": "Absolute public URL of this instance, including the scheme. Used in emails, OAuth/SAML callbacks and the CLI. Update it if you attach a custom domain.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "REDIS_URL",
      "service": "Infisical",
      "description": "Redis connection string. Redis is a hard dependency: it holds the background job queue, distributed locks and rate limit counters, and Infisical refuses to start without it.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "SMTP_PORT",
      "service": "Infisical",
      "description": "Optional. SMTP port. 465 switches the client to implicit TLS; anything else uses STARTTLS.",
      "secret": false,
      "strategy": "default",
      "default": "587"
    },
    {
      "key": "AUTH_SECRET",
      "service": "Infisical",
      "description": "HMAC signing key for session and machine identity JWTs. Changing it signs every user out and invalidates issued identity tokens.",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "ENCRYPTION_KEY",
      "service": "Infisical",
      "description": "Root encryption key for all secrets stored in Postgres. Must be a 16-byte hex string (exactly 32 hex characters), the same shape as `openssl rand -hex 16`. Back this up: without it the database cannot be decrypted, and rotating it needs the documented rotation procedure.",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "DB_CONNECTION_URI",
      "service": "Infisical",
      "description": "Postgres connection string over the private network. Knex migrations run automatically on every boot.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "TELEMETRY_ENABLED",
      "service": "Infisical",
      "description": "Anonymous product telemetry to PostHog. Off by default in this template; set to true to send it.",
      "secret": false,
      "strategy": "default",
      "default": "false"
    },
    {
      "key": "PGDATA",
      "service": "Postgres",
      "description": "Provide a value for PGDATA.",
      "secret": false,
      "strategy": "default",
      "default": "/var/lib/postgresql/data/pgdata"
    },
    {
      "key": "PGHOST",
      "service": "Postgres",
      "description": "Provide a value for PGHOST.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "PGPORT",
      "service": "Postgres",
      "description": "Provide a value for PGPORT.",
      "secret": false,
      "strategy": "default",
      "default": "5432"
    },
    {
      "key": "PGUSER",
      "service": "Postgres",
      "description": "Provide a value for PGUSER.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGDATABASE",
      "service": "Postgres",
      "description": "Provide a value for PGDATABASE.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGPASSWORD",
      "service": "Postgres",
      "description": "Provide a value for PGPASSWORD.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "POSTGRES_DB",
      "service": "Postgres",
      "description": "Database name.",
      "secret": false,
      "strategy": "default",
      "default": "infisical"
    },
    {
      "key": "DATABASE_URL",
      "service": "Postgres",
      "description": "Private-network connection string (IPv6, includes port).",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "POSTGRES_USER",
      "service": "Postgres",
      "description": "Database superuser.",
      "secret": false,
      "strategy": "default",
      "default": "infisical"
    },
    {
      "key": "SSL_CERT_DAYS",
      "service": "Postgres",
      "description": "Provide a value for SSL_CERT_DAYS.",
      "secret": false,
      "strategy": "default",
      "default": "820"
    },
    {
      "key": "POSTGRES_PASSWORD",
      "service": "Postgres",
      "description": "Generated database password.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "DATABASE_PUBLIC_URL",
      "service": "Postgres",
      "description": "Public URL via the TCP proxy (for psql / GUI clients and pg_dump backups).",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "DATABASE_PRIVATE_URL",
      "service": "Postgres",
      "description": "Referenced by Infisical as DB_CONNECTION_URI.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "RAILWAY_DEPLOYMENT_DRAINING_SECONDS",
      "service": "Postgres",
      "description": "Provide a value for RAILWAY_DEPLOYMENT_DRAINING_SECONDS.",
      "secret": false,
      "strategy": "default",
      "default": "60"
    },
    {
      "key": "REDIS_URL",
      "service": "Redis",
      "description": "Private-network connection string (IPv6, includes port, db 0). Referenced by Infisical as REDIS_URL.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "REDIS_PASSWORD",
      "service": "Redis",
      "description": "Generated Redis password (passed to redis-server --requirepass by the start command).",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    }
  ],
  "deploy": {
    "mcp": {
      "server": "railway",
      "tool": "deploy_template",
      "args": {
        "template_code": "infisical-secrets-platform"
      }
    },
    "cli": "railway deploy --template infisical-secrets-platform",
    "api": {
      "method": "POST",
      "path": "/graphql/v2",
      "body": {
        "query": "mutation templateDeploy($input: TemplateDeployV2Input!) { templateDeployV2(input: $input) { projectId workflowId } }",
        "variables": {
          "input": {
            "templateId": "6f8090bd-c884-4d5d-ae47-0ba2e78cd2c7",
            "serializedConfig": {
              "services": {
                "04d9f0dc-5363-4729-96e9-466daf9e5e37": {
                  "icon": "https://avatars.githubusercontent.com/u/107880645?v=4",
                  "name": "Infisical",
                  "deploy": {
                    "healthcheckPath": "/api/status",
                    "restartPolicyType": "ON_FAILURE",
                    "healthcheckTimeout": 300,
                    "restartPolicyMaxRetries": 5
                  },
                  "source": {
                    "image": "infisical/infisical:v0.165.13"
                  },
                  "variables": {
                    "HOST": {
                      "description": "Bind address. Infisical defaults to localhost, which would make the Railway healthcheck fail. Keep 0.0.0.0 so the public domain works. Reaching this service from another Railway service should use the public domain, not *.railway.internal (see README).",
                      "defaultValue": "0.0.0.0"
                    },
                    "PORT": {
                      "description": "Listen port. Railway's healthcheck and edge proxy probe $PORT, so keep it equal to the port Infisical binds (the server reads PORT, upstream default 8080).",
                      "defaultValue": "8080"
                    },
                    "NODE_ENV": {
                      "description": "Production mode. Anything else skips the boot-time Postgres/Redis connectivity check and changes cookie/security defaults.",
                      "defaultValue": "production"
                    },
                    "SITE_URL": {
                      "description": "Absolute public URL of this instance, including the scheme. Used in emails, OAuth/SAML callbacks and the CLI. Update it if you attach a custom domain.",
                      "defaultValue": "https://${{RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "REDIS_URL": {
                      "description": "Redis connection string. Redis is a hard dependency: it holds the background job queue, distributed locks and rate limit counters, and Infisical refuses to start without it.",
                      "defaultValue": "${{Redis.REDIS_URL}}"
                    },
                    "SMTP_HOST": {
                      "isOptional": true,
                      "description": "Optional. SMTP server hostname. Not needed to create the first admin account; required to invite teammates, reset passwords and send notifications.",
                      "defaultValue": ""
                    },
                    "SMTP_PORT": {
                      "isOptional": true,
                      "description": "Optional. SMTP port. 465 switches the client to implicit TLS; anything else uses STARTTLS.",
                      "defaultValue": "587"
                    },
                    "AUTH_SECRET": {
                      "description": "HMAC signing key for session and machine identity JWTs. Changing it signs every user out and invalidates issued identity tokens.",
                      "defaultValue": "{{AUTH_SECRET}}"
                    },
                    "SMTP_PASSWORD": {
                      "isOptional": true,
                      "description": "Optional. SMTP password or API key.",
                      "defaultValue": ""
                    },
                    "SMTP_USERNAME": {
                      "isOptional": true,
                      "description": "Optional. SMTP username.",
                      "defaultValue": ""
                    },
                    "ENCRYPTION_KEY": {
                      "description": "Root encryption key for all secrets stored in Postgres. Must be a 16-byte hex string (exactly 32 hex characters), the same shape as `openssl rand -hex 16`. Back this up: without it the database cannot be decrypted, and rotating it needs the documented rotation procedure.",
                      "defaultValue": "{{ENCRYPTION_KEY}}"
                    },
                    "SMTP_FROM_NAME": {
                      "isOptional": true,
                      "description": "Optional. From name on outgoing mail.",
                      "defaultValue": ""
                    },
                    "DB_CONNECTION_URI": {
                      "description": "Postgres connection string over the private network. Knex migrations run automatically on every boot.",
                      "defaultValue": "${{Postgres.DATABASE_PRIVATE_URL}}"
                    },
                    "SMTP_FROM_ADDRESS": {
                      "isOptional": true,
                      "description": "Optional. From address on outgoing mail.",
                      "defaultValue": ""
                    },
                    "TELEMETRY_ENABLED": {
                      "description": "Anonymous product telemetry to PostHog. Off by default in this template; set to true to send it.",
                      "defaultValue": "false"
                    },
                    "INITIAL_ORGANIZATION_NAME": {
                      "isOptional": true,
                      "description": "Optional. Name given to the organization created with the first admin account. Defaults to \"Admin Org\".",
                      "defaultValue": ""
                    }
                  },
                  "networking": {
                    "tcpProxies": {},
                    "serviceDomains": {
                      "infisical-dom": {
                        "port": 8080
                      }
                    }
                  }
                },
                "4aa1731f-8abb-4633-8e18-790206dfe182": {
                  "icon": "https://devicons.railway.app/i/postgresql.svg",
                  "name": "Postgres",
                  "build": {},
                  "deploy": {
                    "requiredMountPath": "/var/lib/postgresql/data"
                  },
                  "source": {
                    "image": "ghcr.io/railwayapp-templates/postgres-ssl:17"
                  },
                  "variables": {
                    "PGDATA": {
                      "defaultValue": "/var/lib/postgresql/data/pgdata"
                    },
                    "PGHOST": {
                      "defaultValue": "${{RAILWAY_PRIVATE_DOMAIN}}"
                    },
                    "PGPORT": {
                      "defaultValue": "5432"
                    },
                    "PGUSER": {
                      "defaultValue": "${{POSTGRES_USER}}"
                    },
                    "PGDATABASE": {
                      "defaultValue": "${{POSTGRES_DB}}"
                    },
                    "PGPASSWORD": {
                      "defaultValue": "${{POSTGRES_PASSWORD}}"
                    },
                    "POSTGRES_DB": {
                      "description": "Database name.",
                      "defaultValue": "infisical"
                    },
                    "DATABASE_URL": {
                      "description": "Private-network connection string (IPv6, includes port).",
                      "defaultValue": "postgresql://${{PGUSER}}:${{POSTGRES_PASSWORD}}@${{RAILWAY_PRIVATE_DOMAIN}}:5432/${{PGDATABASE}}"
                    },
                    "POSTGRES_USER": {
                      "description": "Database superuser.",
                      "defaultValue": "infisical"
                    },
                    "SSL_CERT_DAYS": {
                      "isOptional": true,
                      "defaultValue": "820"
                    },
                    "POSTGRES_PASSWORD": {
                      "description": "Generated database password.",
                      "defaultValue": "{{POSTGRES_PASSWORD}}"
                    },
                    "DATABASE_PUBLIC_URL": {
                      "description": "Public URL via the TCP proxy (for psql / GUI clients and pg_dump backups).",
                      "defaultValue": "postgresql://${{PGUSER}}:${{POSTGRES_PASSWORD}}@${{RAILWAY_TCP_PROXY_DOMAIN}}:${{RAILWAY_TCP_PROXY_PORT}}/${{PGDATABASE}}"
                    },
                    "DATABASE_PRIVATE_URL": {
                      "description": "Referenced by Infisical as DB_CONNECTION_URI.",
                      "defaultValue": "postgresql://${{PGUSER}}:${{POSTGRES_PASSWORD}}@${{RAILWAY_PRIVATE_DOMAIN}}:5432/${{PGDATABASE}}"
                    },
                    "RAILWAY_DEPLOYMENT_DRAINING_SECONDS": {
                      "isOptional": true,
                      "defaultValue": "60"
                    }
                  },
                  "networking": {
                    "tcpProxies": {
                      "5432": {}
                    },
                    "serviceDomains": {}
                  },
                  "volumeMounts": {
                    "5e34ea6b-849e-4f8d-bc7f-e07f811220b3": {
                      "mountPath": "/var/lib/postgresql/data"
                    }
                  }
                },
                "a95488c1-551c-49f4-b3cb-148df6a50314": {
                  "icon": "https://devicons.railway.app/i/redis.svg",
                  "name": "Redis",
                  "deploy": {
                    "startCommand": "sh -c 'docker-entrypoint.sh redis-server --requirepass \"$REDIS_PASSWORD\" --appendonly yes --save 60 1 --dir /data --maxmemory-policy noeviction --bind :: 0.0.0.0'",
                    "requiredMountPath": "/data",
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 5
                  },
                  "source": {
                    "image": "redis:7.2.4-alpine3.19"
                  },
                  "variables": {
                    "REDIS_URL": {
                      "description": "Private-network connection string (IPv6, includes port, db 0). Referenced by Infisical as REDIS_URL.",
                      "defaultValue": "redis://:${{REDIS_PASSWORD}}@${{RAILWAY_PRIVATE_DOMAIN}}:6379/0"
                    },
                    "REDIS_PASSWORD": {
                      "description": "Generated Redis password (passed to redis-server --requirepass by the start command).",
                      "defaultValue": "{{REDIS_PASSWORD}}"
                    }
                  },
                  "networking": {
                    "tcpProxies": {},
                    "serviceDomains": {}
                  },
                  "volumeMounts": {
                    "9ae552f7-46a5-4a48-94a1-2255efe1ba32": {
                      "mountPath": "/data"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "post_deploy": {
    "healthcheck": {
      "service": "Infisical",
      "method": "GET",
      "path": "/api/status",
      "expect_status": 200
    }
  },
  "resources": {
    "expected_services": 3,
    "needs_volume": true
  },
  "generated_at": "2026-09-21T04:14:46.774Z",
  "generator_version": "0.1.0",
  "status": "validated",
  "validated_at": "2026-09-21T02:20:57.510Z",
  "success_rate_30d": 1,
  "validation": {
    "last_run_id": "run_451bd4ce48a1499db3d1",
    "checks": [
      {
        "name": "workflow_completed",
        "passed": true
      },
      {
        "name": "all_services_deployed",
        "passed": true
      },
      {
        "name": "healthcheck",
        "passed": true
      },
      {
        "name": "stays_up",
        "passed": true
      }
    ],
    "typical_ready_seconds": 131,
    "typical_build_seconds": 0,
    "typical_start_seconds": 52,
    "slowest_service": "Infisical"
  }
}
