{
  "manifest_version": "1.0.0",
  "template": {
    "id": "69130d23-596c-4c48-9612-4ff11d033b35",
    "slug": "logto-or-just-updated-auth0-alternative-",
    "name": "Logto | (Just Updated) Auth0 Alternative, OIDC Identity Server, Admin Login and API Client Ready",
    "description": "Logto auth server: OIDC, OAuth, SSO, generated admin and API client",
    "url": "https://railway.com/deploy/logto-or-just-updated-auth0-alternative-",
    "upstream": {
      "image": "svhd/logto:1.45.0@sha256:9a9aff9423ab7863f00c881c31408e0bfe4def730bac2182d0097367002f5854"
    }
  },
  "status": "unvalidated",
  "validated_at": null,
  "success_rate_30d": null,
  "services": [
    {
      "name": "logto",
      "source": {
        "image": "svhd/logto:1.45.0@sha256:9a9aff9423ab7863f00c881c31408e0bfe4def730bac2182d0097367002f5854"
      },
      "needs_volume": false,
      "http": true
    },
    {
      "name": "postgres",
      "source": {
        "image": "postgres:17@sha256:31ba9c94a03ada9988f2bb493a22a75eef3a185884533e5cdbcd498ea835b08f"
      },
      "needs_volume": true,
      "volume_mount_path": "/var/lib/postgresql",
      "http": false
    },
    {
      "name": "console",
      "source": {
        "image": "caddy@sha256:d8542f48d34a9cf4e4c11a478865229840e87e4c96ea3f439101f31a5d35f75f"
      },
      "needs_volume": false,
      "http": true
    }
  ],
  "required_inputs": [
    {
      "key": "DB_URL",
      "service": "logto",
      "description": "Provide a value for DB_URL.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "ENDPOINT",
      "service": "logto",
      "description": "Provide a value for ENDPOINT.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "M2M_SECRET",
      "service": "logto",
      "description": "Provide a value for M2M_SECRET.",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "ADMIN_ENDPOINT",
      "service": "logto",
      "description": "Provide a value for ADMIN_ENDPOINT.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "ADMIN_PASSWORD",
      "service": "logto",
      "description": "Provide a value for ADMIN_PASSWORD.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "POSTGRES_PASSWORD",
      "service": "postgres",
      "description": "Provide a value for POSTGRES_PASSWORD.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "BACKEND_HOST",
      "service": "console",
      "description": "Provide a value for BACKEND_HOST.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    }
  ],
  "deploy": {
    "mcp": {
      "server": "railway",
      "tool": "deploy_template",
      "args": {
        "template_code": "logto-or-just-updated-auth0-alternative-"
      }
    },
    "cli": "railway deploy --template logto-or-just-updated-auth0-alternative-",
    "api": {
      "method": "POST",
      "path": "/graphql/v2",
      "body": {
        "query": "mutation templateDeploy($input: TemplateDeployV2Input!) { templateDeployV2(input: $input) { projectId workflowId } }",
        "variables": {
          "input": {
            "templateId": "69130d23-596c-4c48-9612-4ff11d033b35",
            "serializedConfig": {
              "buckets": {},
              "services": {
                "5a119d11-9804-4dc9-ab06-c1cc9c3c1e10": {
                  "icon": null,
                  "name": "logto",
                  "deploy": {
                    "startCommand": "/bin/sh -c 'export ADMIN_USERNAME=admin ADMIN_PORT=3002 TRUST_PROXY_HEADER=1; echo Ly8gUmFpbHdheSBib290IHdyYXBwZXIgZm9yIExvZ3RvOiBzZWVkIG9uY2UsIHBpbiB0aGUgTWFuYWdlbWVudCBBUEkgY2xpZW50IHNlY3JldCwKLy8gY3JlYXRlIHRoZSBmaXJzdCBhZG1pbiBmcm9tIHZhcmlhYmxlcywgdGhlbiBoYW5kIG92ZXIgdG8gdGhlIHN0b2NrIHNlcnZlci4KaW1wb3J0IHsgZXhlY1N5bmMsIHNwYXduIH0gZnJvbSAnbm9kZTpjaGlsZF9wcm9jZXNzJzsKaW1wb3J0IHsgcmVhZGRpclN5bmMgfSBmcm9tICdub2RlOmZzJzsKaW1wb3J0IHsgY3JlYXRlUmVxdWlyZSB9IGZyb20gJ25vZGU6bW9kdWxlJzsKCmNvbnN0IHsKICBBRE1JTl9VU0VSTkFNRSwgQURNSU5fUEFTU1dPUkQsIE0yTV9TRUNSRVQsIERCX1VSTCwKICBQT1JUID0gJzgwODAnLCBBRE1JTl9QT1JUID0gJzMwMDInLAp9ID0gcHJvY2Vzcy5lbnY7CmNvbnN0IGxvZyA9IChtKSA9PiBjb25zb2xlLmxvZyhgW3JhaWx3YXldICR7bX1gKTsKY29uc3Qgc2xlZXAgPSAobXMpID0+IG5ldyBQcm9taXNlKChyKSA9PiBzZXRUaW1lb3V0KHIsIG1zKSk7CnByb2Nlc3MuY2hkaXIoJy9ldGMvbG9ndG8nKTsKCmNvbnN0IHBnRGlyID0gcmVhZGRpclN5bmMoJy9ldGMvbG9ndG8vbm9kZV9tb2R1bGVzLy5wbnBtJykuZmluZCgoZCkgPT4gZC5zdGFydHNXaXRoKCdwZ0AnKSk7CmNvbnN0IHsgQ2xpZW50IH0gPSBjcmVhdGVSZXF1aXJlKCcvZXRjL2xvZ3RvLycpKGAvZXRjL2xvZ3RvL25vZGVfbW9kdWxlcy8ucG5wbS8ke3BnRGlyfS9ub2RlX21vZHVsZXMvcGdgKTsKCi8vIDEuIFdhaXQgZm9yIFBvc3RncmVzLCB0aGVuIHNlZWQgKGlkZW1wb3RlbnQgd2l0aCAtLXN3ZSkuCmZvciAobGV0IGkgPSAwOyA7IGkrKykgewogIGNvbnN0IGMgPSBuZXcgQ2xpZW50KHsgY29ubmVjdGlvblN0cmluZzogREJfVVJMIH0pOwogIHRyeSB7IGF3YWl0IGMuY29ubmVjdCgpOyBhd2FpdCBjLnF1ZXJ5KCdzZWxlY3QgMScpOyBhd2FpdCBjLmVuZCgpOyBicmVhazsgfQogIGNhdGNoIChlKSB7IGF3YWl0IGMuZW5kKCkuY2F0Y2goKCkgPT4ge30pOyBpZiAoaSA+IDYwKSB0aHJvdyBlOyBhd2FpdCBzbGVlcCgyMDAwKTsgfQp9CmV4ZWNTeW5jKCducG0gcnVuIGNsaSBkYiBzZWVkIC0tIC0tc3dlJywgeyBzdGRpbzogJ2luaGVyaXQnIH0pOwoKLy8gMi4gUGluIHRoZSBzZWVkZWQgTWFuYWdlbWVudCBBUEkgY2xpZW50IChtLWRlZmF1bHQpIHRvIHRoZSBnZW5lcmF0ZWQgc2VjcmV0Lgpjb25zdCBkYiA9IG5ldyBDbGllbnQoeyBjb25uZWN0aW9uU3RyaW5nOiBEQl9VUkwgfSk7CmF3YWl0IGRiLmNvbm5lY3QoKTsKY29uc3QgdXBkID0gYXdhaXQgZGIucXVlcnkoCiAgInVwZGF0ZSBhcHBsaWNhdGlvbnMgc2V0IHNlY3JldD0kMSB3aGVyZSBpZD0nbS1kZWZhdWx0JyBhbmQgdGVuYW50X2lkPSdhZG1pbiciLCBbTTJNX1NFQ1JFVF0pOwpsb2coYG0tZGVmYXVsdCBzZWNyZXQgcGlubmVkIHJvd3M9JHt1cGQucm93Q291bnR9YCk7CmNvbnN0IHVzZXJzID0gYXdhaXQgZGIucXVlcnkoInNlbGVjdCBjb3VudCgqKTo6aW50IGFzIG4gZnJvbSB1c2VycyB3aGVyZSB0ZW5hbnRfaWQ9J2FkbWluJyIpOwphd2FpdCBkYi5lbmQoKTsKCi8vIDMuIEZpcnN0IGJvb3Qgb25seTogc3RhcnQgdGhlIHNlcnZlciBvbiBsb29wYmFjayBwb3J0cyBhbmQgcmVnaXN0ZXIgdGhlIGFkbWluLgppZiAodXNlcnMucm93c1swXS5uID09PSAwKSB7CiAgbG9nKCdubyBhZG1pbiB5ZXQsIGJvb3RzdHJhcHBpbmcnKTsKICBjb25zdCB0bXAgPSBzcGF3bignbnBtJywgWydzdGFydCddLCB7CiAgICBzdGRpbzogJ2luaGVyaXQnLCBlbnY6IHsgLi4ucHJvY2Vzcy5lbnYsIFBPUlQ6ICczMDAxJywgQURNSU5fUE9SVCB9LAogIH0pOwogIGNvbnN0IGJhc2UgPSBgaHR0cDovL2xvY2FsaG9zdDoke0FETUlOX1BPUlR9YDsKICBmb3IgKGxldCBpID0gMDsgOyBpKyspIHsKICAgIHRyeSB7IGlmICgoYXdhaXQgZmV0Y2goYCR7YmFzZX0vYXBpL3N0YXR1c2ApKS5zdGF0dXMgPT09IDIwNCkgYnJlYWs7IH0gY2F0Y2gge30KICAgIGlmIChpID4gOTApIHRocm93IG5ldyBFcnJvcignbG9ndG8gZGlkIG5vdCBjb21lIHVwIGZvciBib290c3RyYXAnKTsKICAgIGF3YWl0IHNsZWVwKDEwMDApOwogIH0KICBjb25zdCBqYXIgPSBuZXcgTWFwKCk7CiAgY29uc3QgY2FsbCA9IGFzeW5jIChtZXRob2QsIHBhdGgsIGJvZHkpID0+IHsKICAgIGNvbnN0IHJlcyA9IGF3YWl0IGZldGNoKGJhc2UgKyBwYXRoLCB7CiAgICAgIG1ldGhvZCwgcmVkaXJlY3Q6ICdtYW51YWwnLAogICAgICBoZWFkZXJzOiB7CiAgICAgICAgY29va2llOiBbLi4uamFyXS5tYXAoKFtrLCB2XSkgPT4gYCR7a309JHt2fWApLmpvaW4oJzsgJyksCiAgICAgICAgLi4uKGJvZHkgPyB7ICdjb250ZW50LXR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSA6IHt9KSwKICAgICAgfSwKICAgICAgYm9keTogYm9keSA/IEpTT04uc3RyaW5naWZ5KGJvZHkpIDogdW5kZWZpbmVkLAogICAgfSk7CiAgICBmb3IgKGNvbnN0IGMgb2YgcmVzLmhlYWRlcnMuZ2V0U2V0Q29va2llKCkpIHsKICAgICAgY29uc3QgW2t2XSA9IGMuc3BsaXQoJzsnKTsgY29uc3QgaSA9IGt2LmluZGV4T2YoJz0nKTsKICAgICAgamFyLnNldChrdi5zbGljZSgwLCBpKSwga3Yuc2xpY2UoaSArIDEpKTsKICAgIH0KICAgIGlmIChyZXMuc3RhdHVzID49IDQwMCkgdGhyb3cgbmV3IEVycm9yKGAke21ldGhvZH0gJHtwYXRofTogJHtyZXMuc3RhdHVzfSAke2F3YWl0IHJlcy50ZXh0KCl9YCk7CiAgICByZXR1cm4gcmVzOwogIH07CiAgYXdhaXQgY2FsbCgnR0VUJywgJy9vaWRjL2F1dGg/JyArIG5ldyBVUkxTZWFyY2hQYXJhbXMoewogICAgY2xpZW50X2lkOiAnYWRtaW4tY29uc29sZScsIHJlZGlyZWN0X3VyaTogYCR7YmFzZX0vY29uc29sZS9jYWxsYmFja2AsIHJlc3BvbnNlX3R5cGU6ICdjb2RlJywKICAgIHNjb3BlOiAnb3BlbmlkIG9mZmxpbmVfYWNjZXNzIHByb2ZpbGUnLCBwcm9tcHQ6ICdjb25zZW50Jywgc3RhdGU6ICdib290c3RyYXAnLAogICAgY29kZV9jaGFsbGVuZ2U6ICdFOU1lbGhvYTJPd3ZGckVNVEpndUNIYW9lSzF0OFVSV2J1R0pTc3R3LWNNJywgY29kZV9jaGFsbGVuZ2VfbWV0aG9kOiAnUzI1NicsCiAgfSkpOwogIGF3YWl0IGNhbGwoJ1BVVCcsICcvYXBpL2V4cGVyaWVuY2UnLCB7IGludGVyYWN0aW9uRXZlbnQ6ICdSZWdpc3RlcicgfSk7CiAgY29uc3QgdiA9IGF3YWl0IGNhbGwoJ1BPU1QnLCAnL2FwaS9leHBlcmllbmNlL3ZlcmlmaWNhdGlvbi9uZXctcGFzc3dvcmQtaWRlbnRpdHknLCB7CiAgICBpZGVudGlmaWVyOiB7IHR5cGU6ICd1c2VybmFtZScsIHZhbHVlOiBBRE1JTl9VU0VSTkFNRSB9LCBwYXNzd29yZDogQURNSU5fUEFTU1dPUkQsCiAgfSk7CiAgY29uc3QgeyB2ZXJpZmljYXRpb25JZCB9ID0gYXdhaXQgdi5qc29uKCk7CiAgYXdhaXQgY2FsbCgnUE9TVCcsICcvYXBpL2V4cGVyaWVuY2UvaWRlbnRpZmljYXRpb24nLCB7IHZlcmlmaWNhdGlvbklkIH0pOwogIGF3YWl0IGNhbGwoJ1BPU1QnLCAnL2FwaS9leHBlcmllbmNlL3N1Ym1pdCcpOwogIGxvZyhgYWRtaW4gJHtBRE1JTl9VU0VSTkFNRX0gY3JlYXRlZGApOwogIHRtcC5raWxsKCdTSUdURVJNJyk7CiAgYXdhaXQgbmV3IFByb21pc2UoKHIpID0+IHRtcC5vbignZXhpdCcsIHIpKTsKfSBlbHNlIHsKICBsb2coJ2FkbWluIGV4aXN0cywgc2tpcHBpbmcgYm9vdHN0cmFwJyk7Cn0KCi8vIDQuIFJlYWwgc2VydmVyLgpjb25zdCBzcnYgPSBzcGF3bignbnBtJywgWydzdGFydCddLCB7IHN0ZGlvOiAnaW5oZXJpdCcsIGVudjogcHJvY2Vzcy5lbnYgfSk7CmZvciAoY29uc3QgcyBvZiBbJ1NJR1RFUk0nLCAnU0lHSU5UJ10pIHByb2Nlc3Mub24ocywgKCkgPT4gc3J2LmtpbGwocykpOwpzcnYub24oJ2V4aXQnLCAoY29kZSkgPT4gcHJvY2Vzcy5leGl0KGNvZGUgPz8gMCkpOwo= | base64 -d > /etc/logto/boot.mjs && exec node /etc/logto/boot.mjs'",
                    "healthcheckPath": "/api/status",
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "svhd/logto:1.45.0@sha256:9a9aff9423ab7863f00c881c31408e0bfe4def730bac2182d0097367002f5854"
                  },
                  "variables": {
                    "DB_URL": {
                      "isOptional": false,
                      "defaultValue": "postgres://postgres:${{postgres.POSTGRES_PASSWORD}}@${{postgres.RAILWAY_PRIVATE_DOMAIN}}:5432/postgres"
                    },
                    "ENDPOINT": {
                      "isOptional": false,
                      "defaultValue": "https://${{RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "M2M_SECRET": {
                      "isOptional": false,
                      "defaultValue": "{{M2M_SECRET}}"
                    },
                    "ADMIN_ENDPOINT": {
                      "isOptional": false,
                      "defaultValue": "https://${{RAILWAY_SERVICE_CONSOLE_URL}}"
                    },
                    "ADMIN_PASSWORD": {
                      "isOptional": false,
                      "defaultValue": "{{ADMIN_PASSWORD}}"
                    }
                  },
                  "networking": {
                    "serviceDomains": {
                      "<hasDomain>:8080": {
                        "port": 8080
                      }
                    }
                  }
                },
                "6d36dee7-b631-414a-8c3a-0ee35fcb1724": {
                  "icon": null,
                  "name": "postgres",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "postgres:17@sha256:31ba9c94a03ada9988f2bb493a22a75eef3a185884533e5cdbcd498ea835b08f"
                  },
                  "variables": {
                    "POSTGRES_PASSWORD": {
                      "isOptional": false,
                      "defaultValue": "{{POSTGRES_PASSWORD}}"
                    }
                  },
                  "volumeMounts": {
                    "6d36dee7-b631-414a-8c3a-0ee35fcb1724": {
                      "mountPath": "/var/lib/postgresql"
                    }
                  }
                },
                "a303a757-1bb0-421f-9a50-f2a01bb01817": {
                  "icon": null,
                  "name": "console",
                  "deploy": {
                    "startCommand": "/bin/sh -c 'printf \":%s {\\n\\thandle /railway_health {\\n\\t\\trespond \\\"ok\\\" 200\\n\\t}\\n\\thandle {\\n\\t\\treverse_proxy %s:3002 {\\n\\t\\t\\theader_up X-Forwarded-Proto https\\n\\t\\t}\\n\\t}\\n}\\n\" \"$PORT\" \"$BACKEND_HOST\" > /etc/caddy/Caddyfile && echo \"[railway] port=$PORT upstream=$BACKEND_HOST:3002\" && exec caddy run --config /etc/caddy/Caddyfile --adapter caddyfile'",
                    "healthcheckPath": "/railway_health",
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "caddy@sha256:d8542f48d34a9cf4e4c11a478865229840e87e4c96ea3f439101f31a5d35f75f"
                  },
                  "variables": {
                    "BACKEND_HOST": {
                      "isOptional": false,
                      "defaultValue": "${{logto.RAILWAY_PRIVATE_DOMAIN}}"
                    }
                  },
                  "networking": {
                    "serviceDomains": {
                      "<hasDomain>:8080": {
                        "port": 8080
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "post_deploy": {
    "healthcheck": {
      "service": "logto",
      "method": "GET",
      "path": "/api/status",
      "expect_status": 200
    }
  },
  "resources": {
    "expected_services": 3,
    "needs_volume": true
  },
  "generated_at": "2026-10-30T04:14:55.026Z",
  "generator_version": "0.1.0"
}
