{
  "manifest_version": "1.0.0",
  "template": {
    "id": "1565a185-1212-4026-8afc-7edb0a465f5f",
    "slug": "glitchtip-error-tracking",
    "name": "GlitchTip | Open Source Sentry-Compatible Error Tracking",
    "description": "Sentry-compatible error tracking. No S3 to set up, private-network ingest",
    "url": "https://railway.com/deploy/glitchtip-error-tracking",
    "upstream": {
      "image": "glitchtip/glitchtip:6.2.6"
    }
  },
  "services": [
    {
      "name": "Redis",
      "source": {
        "image": "redis:8.10.1-alpine"
      },
      "needs_volume": false,
      "http": false
    },
    {
      "name": "Postgres",
      "source": {
        "image": "postgres:18.6-alpine"
      },
      "needs_volume": true,
      "volume_mount_path": "/var/lib/postgresql",
      "http": false
    },
    {
      "name": "GlitchTip",
      "source": {
        "image": "glitchtip/glitchtip:6.2.6"
      },
      "needs_volume": true,
      "volume_mount_path": "/code/uploads",
      "http": true
    }
  ],
  "required_inputs": [
    {
      "key": "PORT",
      "service": "Redis",
      "description": "Port Redis listens on. Reachable only inside this project's private network.",
      "secret": false,
      "strategy": "default",
      "default": "6379"
    },
    {
      "key": "PORT",
      "service": "Postgres",
      "description": "Port Postgres listens on.",
      "secret": false,
      "strategy": "default",
      "default": "5432"
    },
    {
      "key": "POSTGRES_DB",
      "service": "Postgres",
      "description": "Database created on first boot; GlitchTip runs its migrations here.",
      "secret": false,
      "strategy": "default",
      "default": "glitchtip"
    },
    {
      "key": "POSTGRES_USER",
      "service": "Postgres",
      "description": "Superuser GlitchTip connects as.",
      "secret": false,
      "strategy": "default",
      "default": "postgres"
    },
    {
      "key": "POSTGRES_PASSWORD",
      "service": "Postgres",
      "description": "Generated once. The alphabet is alphanumeric on purpose: the default character set can emit / or @, either of which breaks the connection URL it is substituted into.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "PORT",
      "service": "GlitchTip",
      "description": "Port the web server listens on.",
      "secret": false,
      "strategy": "default",
      "default": "8000"
    },
    {
      "key": "EMAIL_URL",
      "service": "GlitchTip",
      "description": "Where mail goes. The default writes it to the deploy log, so invitations and alerts are visible rather than silently lost. Set an SMTP URL such as smtp://user:pass@host:587 to actually send it.",
      "secret": false,
      "strategy": "default",
      "default": "consolemail://"
    },
    {
      "key": "REDIS_URL",
      "service": "GlitchTip",
      "description": "Same broker under the name older GlitchTip releases read. Keep the two in sync.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "SECRET_KEY",
      "service": "GlitchTip",
      "description": "Django signing key: sessions, password-reset links, CSRF. Generated once per deployment. Changing it logs everyone out.",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "VALKEY_URL",
      "service": "GlitchTip",
      "description": "Celery broker. Tasks here are all re-derivable from Postgres, so nothing is lost if Redis restarts.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "DATABASE_URL",
      "service": "GlitchTip",
      "description": "Where issues, events, users and organizations are stored, over the private network.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "ENABLE_ADMIN",
      "service": "GlitchTip",
      "description": "Django admin interface. Off by default; it is a second, much broader way into the same data.",
      "secret": false,
      "strategy": "default",
      "default": "False"
    },
    {
      "key": "GRANIAN_HOST",
      "service": "GlitchTip",
      "description": "Address the web server binds. The IPv6 wildcard is what makes glitchtip.railway.internal reachable, so apps in this project can report errors over the private network instead of the public domain. Granian's own default, 0.0.0.0, answers only the public domain.",
      "secret": false,
      "strategy": "default",
      "default": "::"
    },
    {
      "key": "ENABLE_OPENAPI",
      "service": "GlitchTip",
      "description": "Publicly served API schema and docs page. Off by default.",
      "secret": false,
      "strategy": "default",
      "default": "False"
    },
    {
      "key": "GRANIAN_WORKERS",
      "service": "GlitchTip",
      "description": "Web worker processes. Raise it if the UI is slow while events are being ingested; each one costs memory.",
      "secret": false,
      "strategy": "default",
      "default": "1"
    },
    {
      "key": "RAILWAY_RUN_UID",
      "service": "GlitchTip",
      "description": "The image runs as uid 5000 and Railway attaches the volume owned by root, so without this the first uploaded artifact fails to write.",
      "secret": false,
      "strategy": "default",
      "default": "0"
    },
    {
      "key": "GLITCHTIP_DOMAIN",
      "service": "GlitchTip",
      "description": "Public URL of this deployment. It goes into every DSN you copy into an application, so it must include the scheme — without it the DSN your SDK gets is unusable.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "DEFAULT_FROM_EMAIL",
      "service": "GlitchTip",
      "description": "From address on invitations and alert emails.",
      "secret": false,
      "strategy": "default",
      "default": "glitchtip@example.com"
    },
    {
      "key": "GLITCHTIP_EMBED_WORKER",
      "service": "GlitchTip",
      "description": "Runs the background worker inside the web container. Turn it off only if you add a separate worker service, otherwise events are accepted and never processed.",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "ENABLE_USER_REGISTRATION",
      "service": "GlitchTip",
      "description": "After the first account is created, signup is closed — otherwise anyone who finds the URL can register in your error tracker. Register your own account as soon as the deployment is green.",
      "secret": false,
      "strategy": "default",
      "default": "False"
    },
    {
      "key": "ENABLE_ORGANIZATION_CREATION",
      "service": "GlitchTip",
      "description": "Stops non-owner users from creating additional organizations. The first owner can still create their own.",
      "secret": false,
      "strategy": "default",
      "default": "False"
    },
    {
      "key": "GLITCHTIP_MAX_EVENT_LIFE_DAYS",
      "service": "GlitchTip",
      "description": "How long events are kept before deletion. This is the main lever on database size — events grow with error volume, not with UI traffic.",
      "secret": false,
      "strategy": "default",
      "default": "90"
    }
  ],
  "deploy": {
    "mcp": {
      "server": "railway",
      "tool": "deploy_template",
      "args": {
        "template_code": "glitchtip-error-tracking"
      }
    },
    "cli": "railway deploy --template glitchtip-error-tracking",
    "api": {
      "method": "POST",
      "path": "/graphql/v2",
      "body": {
        "query": "mutation templateDeploy($input: TemplateDeployV2Input!) { templateDeployV2(input: $input) { projectId workflowId } }",
        "variables": {
          "input": {
            "templateId": "1565a185-1212-4026-8afc-7edb0a465f5f",
            "serializedConfig": {
              "buckets": {},
              "services": {
                "027ba336-90aa-4287-a2b5-2ccdb4f37bd3": {
                  "icon": "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/redis.svg",
                  "name": "Redis",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 2
                  },
                  "source": {
                    "image": "redis:8.10.1-alpine"
                  },
                  "variables": {
                    "PORT": {
                      "isOptional": false,
                      "description": "Port Redis listens on. Reachable only inside this project's private network.",
                      "defaultValue": "6379"
                    }
                  },
                  "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."
                  }
                },
                "4cced276-fd06-4997-8524-da9bb0e48123": {
                  "icon": "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/postgresql.svg",
                  "name": "Postgres",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 2
                  },
                  "source": {
                    "image": "postgres:18.6-alpine"
                  },
                  "variables": {
                    "PORT": {
                      "isOptional": false,
                      "description": "Port Postgres listens on.",
                      "defaultValue": "5432"
                    },
                    "POSTGRES_DB": {
                      "isOptional": false,
                      "description": "Database created on first boot; GlitchTip runs its migrations here.",
                      "defaultValue": "glitchtip"
                    },
                    "POSTGRES_USER": {
                      "isOptional": false,
                      "description": "Superuser GlitchTip connects as.",
                      "defaultValue": "postgres"
                    },
                    "POSTGRES_PASSWORD": {
                      "isOptional": false,
                      "description": "Generated once. The alphabet is alphanumeric on purpose: the default character set can emit / or @, either of which breaks the connection URL it is substituted into.",
                      "defaultValue": "{{POSTGRES_PASSWORD}}"
                    }
                  },
                  "volumeMounts": {
                    "4cced276-fd06-4997-8524-da9bb0e48123": {
                      "mountPath": "/var/lib/postgresql"
                    }
                  }
                },
                "63865335-f010-4863-93a0-509d5535cee9": {
                  "icon": "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glitchtip.svg",
                  "name": "GlitchTip",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": "/_health/",
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 2
                  },
                  "source": {
                    "image": "glitchtip/glitchtip:6.2.6"
                  },
                  "variables": {
                    "PORT": {
                      "isOptional": false,
                      "description": "Port the web server listens on.",
                      "defaultValue": "8000"
                    },
                    "EMAIL_URL": {
                      "isOptional": false,
                      "description": "Where mail goes. The default writes it to the deploy log, so invitations and alerts are visible rather than silently lost. Set an SMTP URL such as smtp://user:pass@host:587 to actually send it.",
                      "defaultValue": "consolemail://"
                    },
                    "REDIS_URL": {
                      "isOptional": false,
                      "description": "Same broker under the name older GlitchTip releases read. Keep the two in sync.",
                      "defaultValue": "redis://${{Redis.RAILWAY_PRIVATE_DOMAIN}}:6379/0"
                    },
                    "SECRET_KEY": {
                      "isOptional": false,
                      "description": "Django signing key: sessions, password-reset links, CSRF. Generated once per deployment. Changing it logs everyone out.",
                      "defaultValue": "{{SECRET_KEY}}"
                    },
                    "VALKEY_URL": {
                      "isOptional": false,
                      "description": "Celery broker. Tasks here are all re-derivable from Postgres, so nothing is lost if Redis restarts.",
                      "defaultValue": "redis://${{Redis.RAILWAY_PRIVATE_DOMAIN}}:6379/0"
                    },
                    "DATABASE_URL": {
                      "isOptional": false,
                      "description": "Where issues, events, users and organizations are stored, over the private network.",
                      "defaultValue": "postgresql://postgres:${{Postgres.POSTGRES_PASSWORD}}@${{Postgres.RAILWAY_PRIVATE_DOMAIN}}:5432/glitchtip"
                    },
                    "ENABLE_ADMIN": {
                      "isOptional": false,
                      "description": "Django admin interface. Off by default; it is a second, much broader way into the same data.",
                      "defaultValue": "False"
                    },
                    "GRANIAN_HOST": {
                      "isOptional": false,
                      "description": "Address the web server binds. The IPv6 wildcard is what makes glitchtip.railway.internal reachable, so apps in this project can report errors over the private network instead of the public domain. Granian's own default, 0.0.0.0, answers only the public domain.",
                      "defaultValue": "::"
                    },
                    "ENABLE_OPENAPI": {
                      "isOptional": false,
                      "description": "Publicly served API schema and docs page. Off by default.",
                      "defaultValue": "False"
                    },
                    "GRANIAN_WORKERS": {
                      "isOptional": false,
                      "description": "Web worker processes. Raise it if the UI is slow while events are being ingested; each one costs memory.",
                      "defaultValue": "1"
                    },
                    "RAILWAY_RUN_UID": {
                      "isOptional": false,
                      "description": "The image runs as uid 5000 and Railway attaches the volume owned by root, so without this the first uploaded artifact fails to write.",
                      "defaultValue": "0"
                    },
                    "GLITCHTIP_DOMAIN": {
                      "isOptional": false,
                      "description": "Public URL of this deployment. It goes into every DSN you copy into an application, so it must include the scheme — without it the DSN your SDK gets is unusable.",
                      "defaultValue": "https://${{RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "DEFAULT_FROM_EMAIL": {
                      "isOptional": false,
                      "description": "From address on invitations and alert emails.",
                      "defaultValue": "glitchtip@example.com"
                    },
                    "GLITCHTIP_EMBED_WORKER": {
                      "isOptional": false,
                      "description": "Runs the background worker inside the web container. Turn it off only if you add a separate worker service, otherwise events are accepted and never processed.",
                      "defaultValue": "true"
                    },
                    "ENABLE_USER_REGISTRATION": {
                      "isOptional": false,
                      "description": "After the first account is created, signup is closed — otherwise anyone who finds the URL can register in your error tracker. Register your own account as soon as the deployment is green.",
                      "defaultValue": "False"
                    },
                    "ENABLE_ORGANIZATION_CREATION": {
                      "isOptional": false,
                      "description": "Stops non-owner users from creating additional organizations. The first owner can still create their own.",
                      "defaultValue": "False"
                    },
                    "GLITCHTIP_MAX_EVENT_LIFE_DAYS": {
                      "isOptional": false,
                      "description": "How long events are kept before deletion. This is the main lever on database size — events grow with error volume, not with UI traffic.",
                      "defaultValue": "90"
                    }
                  },
                  "networking": {
                    "serviceDomains": {
                      "<hasDomain>:8000": {
                        "port": 8000
                      }
                    }
                  },
                  "volumeMounts": {
                    "63865335-f010-4863-93a0-509d5535cee9": {
                      "mountPath": "/code/uploads"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "post_deploy": {
    "healthcheck": {
      "service": "GlitchTip",
      "method": "GET",
      "path": "/_health/",
      "expect_status": 200
    }
  },
  "resources": {
    "expected_services": 3,
    "needs_volume": true
  },
  "generated_at": "2026-09-19T10:14:38.021Z",
  "generator_version": "0.1.0",
  "status": "validated",
  "validated_at": "2026-09-17T10:04:53.350Z",
  "success_rate_30d": 1,
  "validation": {
    "last_run_id": "run_5eedde47eb26451ab364",
    "checks": [
      {
        "name": "workflow_completed",
        "passed": true
      },
      {
        "name": "all_services_deployed",
        "passed": true
      },
      {
        "name": "healthcheck",
        "passed": true
      },
      {
        "name": "stays_up",
        "passed": true
      }
    ],
    "typical_ready_seconds": 133,
    "typical_build_seconds": 0,
    "typical_start_seconds": 30,
    "slowest_service": "GlitchTip"
  }
}
