{
  "manifest_version": "1.0.0",
  "template": {
    "id": "f082b1c8-efcc-47bf-b9f4-5b0d17f9faa9",
    "slug": "private-docker-registry",
    "name": "Docker Registry",
    "description": "A private server for storing and sharing container images",
    "url": "https://railway.com/deploy/private-docker-registry",
    "upstream": {
      "repo_url": "https://github.com/gridalpha/docker-registry-railway"
    }
  },
  "services": [
    {
      "name": "registry",
      "source": {
        "repo": "https://github.com/gridalpha/docker-registry-railway"
      },
      "needs_volume": false,
      "http": true
    },
    {
      "name": "Redis",
      "source": {
        "image": "redis:8.2"
      },
      "needs_volume": true,
      "volume_mount_path": "/data",
      "http": false
    },
    {
      "name": "registry-ui",
      "source": {
        "image": "joxit/docker-registry-ui:latest"
      },
      "needs_volume": false,
      "http": true
    }
  ],
  "required_inputs": [
    {
      "key": "PORT",
      "service": "registry",
      "description": "Debug port the health check probes",
      "secret": false,
      "strategy": "default",
      "default": "5001"
    },
    {
      "key": "S3_BUCKET",
      "service": "registry",
      "description": "Bucket holding all image data",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "S3_REGION",
      "service": "registry",
      "description": "Bucket placement region",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "REDIS_HOST",
      "service": "registry",
      "description": "Blob descriptor cache host",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "REDIS_PORT",
      "service": "registry",
      "description": "Blob descriptor cache port",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "REDIS_USER",
      "service": "registry",
      "description": "Blob descriptor cache username",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "S3_ENDPOINT",
      "service": "registry",
      "description": "Object storage endpoint URL",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "REDIS_PASSWORD",
      "service": "registry",
      "description": "Blob descriptor cache password",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "S3_ACCESS_KEY_ID",
      "service": "registry",
      "description": "Bucket access key",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "REGISTRY_PASSWORD",
      "service": "registry",
      "description": "Password docker login accepts",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "REGISTRY_USERNAME",
      "service": "registry",
      "description": "Account docker login accepts",
      "secret": false,
      "strategy": "default",
      "default": "admin"
    },
    {
      "key": "REGISTRY_LOG_LEVEL",
      "service": "registry",
      "description": "Log verbosity; debug is very loud",
      "secret": false,
      "strategy": "default",
      "default": "info"
    },
    {
      "key": "REGISTRY_HTTP_SECRET",
      "service": "registry",
      "description": "Signs in-progress upload state",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "S3_SECRET_ACCESS_KEY",
      "service": "registry",
      "description": "Bucket secret key",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "RAILWAY_DOCKERFILE_PATH",
      "service": "registry",
      "description": "Dockerfile used to build the service",
      "secret": false,
      "strategy": "default",
      "default": "Dockerfile"
    },
    {
      "key": "REGISTRY_HTTP_DRAINTIMEOUT",
      "service": "registry",
      "description": "Graceful shutdown window",
      "secret": false,
      "strategy": "default",
      "default": "25s"
    },
    {
      "key": "RAILWAY_DEPLOYMENT_DRAINING_SECONDS",
      "service": "registry",
      "description": "Time allowed to drain connections",
      "secret": false,
      "strategy": "default",
      "default": "30"
    },
    {
      "key": "REDISHOST",
      "service": "Redis",
      "description": "Data panel alias, not read by the server",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "REDISPORT",
      "service": "Redis",
      "description": "Data panel alias, not read by the server",
      "secret": false,
      "strategy": "default",
      "default": "6379"
    },
    {
      "key": "REDISUSER",
      "service": "Redis",
      "description": "Data panel alias, not read by the server",
      "secret": false,
      "strategy": "default",
      "default": "default"
    },
    {
      "key": "REDIS_URL",
      "service": "Redis",
      "description": "Private connection string",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "REDISPASSWORD",
      "service": "Redis",
      "description": "Data panel alias, not read by the server",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "REDIS_PASSWORD",
      "service": "Redis",
      "description": "Auth password, read by the server",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "RAILWAY_VOLUME_MOUNT_PATH",
      "service": "Redis",
      "description": "Where the Redis volume is mounted",
      "secret": false,
      "strategy": "default",
      "default": "/data"
    },
    {
      "key": "PORT",
      "service": "registry-ui",
      "description": "HTTP port nginx listens on",
      "secret": false,
      "strategy": "default",
      "default": "8080"
    },
    {
      "key": "PULL_URL",
      "service": "registry-ui",
      "description": "Hostname shown in pull commands",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "DELETE_IMAGES",
      "service": "registry-ui",
      "description": "Offer tag deletion in the UI",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "REGISTRY_TITLE",
      "service": "registry-ui",
      "description": "Name shown in the UI header",
      "secret": false,
      "strategy": "default",
      "default": "Docker Registry"
    },
    {
      "key": "SINGLE_REGISTRY",
      "service": "registry-ui",
      "description": "Hide the registry switcher",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "REGISTRY_SECURED",
      "service": "registry-ui",
      "description": "Registry requires authentication",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "SHOW_TAG_HISTORY",
      "service": "registry-ui",
      "description": "Show image history and layers",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "NGINX_LISTEN_PORT",
      "service": "registry-ui",
      "description": "Same port, read by the image entrypoint",
      "secret": false,
      "strategy": "default",
      "default": "8080"
    },
    {
      "key": "TAGLIST_PAGE_SIZE",
      "service": "registry-ui",
      "description": "Tags shown per page",
      "secret": false,
      "strategy": "default",
      "default": "100"
    },
    {
      "key": "SHOW_CONTENT_DIGEST",
      "service": "registry-ui",
      "description": "Show manifest digests in tag lists",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "NGINX_PROXY_PASS_URL",
      "service": "registry-ui",
      "description": "Private registry upstream",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "SHOW_CATALOG_NB_TAGS",
      "service": "registry-ui",
      "description": "Show tag counts on the catalog",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "CATALOG_ELEMENTS_LIMIT",
      "service": "registry-ui",
      "description": "Maximum repositories fetched",
      "secret": false,
      "strategy": "default",
      "default": "1000"
    },
    {
      "key": "CATALOG_DEFAULT_EXPANDED",
      "service": "registry-ui",
      "description": "Expand repository groups by default",
      "secret": false,
      "strategy": "default",
      "default": "1"
    }
  ],
  "deploy": {
    "mcp": {
      "server": "railway",
      "tool": "deploy_template",
      "args": {
        "template_code": "private-docker-registry"
      }
    },
    "cli": "railway deploy --template private-docker-registry",
    "api": {
      "method": "POST",
      "path": "/graphql/v2",
      "body": {
        "query": "mutation templateDeploy($input: TemplateDeployV2Input!) { templateDeployV2(input: $input) { projectId workflowId } }",
        "variables": {
          "input": {
            "templateId": "f082b1c8-efcc-47bf-b9f4-5b0d17f9faa9",
            "serializedConfig": {
              "buckets": {
                "e00d7b36-b316-42ed-8ada-40fa9cd62c7e": {
                  "name": "registry-storage"
                }
              },
              "services": {
                "08ad0b1e-d6f4-4791-91c7-6123d3296d98": {
                  "icon": "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/distribution.svg",
                  "name": "registry",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "repo": "https://github.com/gridalpha/docker-registry-railway",
                    "rootDirectory": null
                  },
                  "variables": {
                    "PORT": {
                      "isOptional": false,
                      "description": "Debug port the health check probes",
                      "defaultValue": "5001"
                    },
                    "S3_BUCKET": {
                      "isOptional": false,
                      "description": "Bucket holding all image data",
                      "defaultValue": "${{registry-storage.BUCKET}}"
                    },
                    "S3_REGION": {
                      "isOptional": false,
                      "description": "Bucket placement region",
                      "defaultValue": "${{registry-storage.REGION}}"
                    },
                    "REDIS_HOST": {
                      "isOptional": false,
                      "description": "Blob descriptor cache host",
                      "defaultValue": "${{Redis.REDISHOST}}"
                    },
                    "REDIS_PORT": {
                      "isOptional": false,
                      "description": "Blob descriptor cache port",
                      "defaultValue": "${{Redis.REDISPORT}}"
                    },
                    "REDIS_USER": {
                      "isOptional": false,
                      "description": "Blob descriptor cache username",
                      "defaultValue": "${{Redis.REDISUSER}}"
                    },
                    "S3_ENDPOINT": {
                      "isOptional": false,
                      "description": "Object storage endpoint URL",
                      "defaultValue": "${{registry-storage.ENDPOINT}}"
                    },
                    "REDIS_PASSWORD": {
                      "isOptional": false,
                      "description": "Blob descriptor cache password",
                      "defaultValue": "${{Redis.REDISPASSWORD}}"
                    },
                    "S3_ACCESS_KEY_ID": {
                      "isOptional": false,
                      "description": "Bucket access key",
                      "defaultValue": "${{registry-storage.ACCESS_KEY_ID}}"
                    },
                    "REGISTRY_PASSWORD": {
                      "isOptional": false,
                      "description": "Password docker login accepts",
                      "defaultValue": "{{REGISTRY_PASSWORD}}"
                    },
                    "REGISTRY_USERNAME": {
                      "isOptional": false,
                      "description": "Account docker login accepts",
                      "defaultValue": "admin"
                    },
                    "REGISTRY_LOG_LEVEL": {
                      "isOptional": false,
                      "description": "Log verbosity; debug is very loud",
                      "defaultValue": "info"
                    },
                    "REGISTRY_HTTP_SECRET": {
                      "isOptional": false,
                      "description": "Signs in-progress upload state",
                      "defaultValue": "{{REGISTRY_HTTP_SECRET}}"
                    },
                    "S3_SECRET_ACCESS_KEY": {
                      "isOptional": false,
                      "description": "Bucket secret key",
                      "defaultValue": "${{registry-storage.SECRET_ACCESS_KEY}}"
                    },
                    "RAILWAY_DOCKERFILE_PATH": {
                      "isOptional": false,
                      "description": "Dockerfile used to build the service",
                      "defaultValue": "Dockerfile"
                    },
                    "REGISTRY_HTTP_DRAINTIMEOUT": {
                      "isOptional": false,
                      "description": "Graceful shutdown window",
                      "defaultValue": "25s"
                    },
                    "RAILWAY_DEPLOYMENT_DRAINING_SECONDS": {
                      "isOptional": false,
                      "description": "Time allowed to drain connections",
                      "defaultValue": "30"
                    }
                  },
                  "networking": {
                    "serviceDomains": {
                      "<hasDomain>:5000": {
                        "port": 5000
                      }
                    }
                  }
                },
                "89b550e1-84f6-4e9b-80d6-55ee8b481779": {
                  "icon": "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/redis.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"
                  },
                  "variables": {
                    "REDISHOST": {
                      "isOptional": false,
                      "description": "Data panel alias, not read by the server",
                      "defaultValue": "${{RAILWAY_PRIVATE_DOMAIN}}"
                    },
                    "REDISPORT": {
                      "isOptional": false,
                      "description": "Data panel alias, not read by the server",
                      "defaultValue": "6379"
                    },
                    "REDISUSER": {
                      "isOptional": false,
                      "description": "Data panel alias, not read by the server",
                      "defaultValue": "default"
                    },
                    "REDIS_URL": {
                      "isOptional": false,
                      "description": "Private connection string",
                      "defaultValue": "redis://${{REDISUSER}}:${{REDIS_PASSWORD}}@${{REDISHOST}}:${{REDISPORT}}"
                    },
                    "REDISPASSWORD": {
                      "isOptional": false,
                      "description": "Data panel alias, not read by the server",
                      "defaultValue": "${{REDIS_PASSWORD}}"
                    },
                    "REDIS_PASSWORD": {
                      "isOptional": false,
                      "description": "Auth password, read by the server",
                      "defaultValue": "{{REDIS_PASSWORD}}"
                    },
                    "RAILWAY_VOLUME_MOUNT_PATH": {
                      "description": "Where the Redis volume is mounted",
                      "defaultValue": "/data"
                    }
                  },
                  "volumeMounts": {
                    "89b550e1-84f6-4e9b-80d6-55ee8b481779": {
                      "mountPath": "/data"
                    }
                  }
                },
                "9b214640-48d8-4a83-93cf-a28b9a24e9f7": {
                  "icon": "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/docker-moby.svg",
                  "name": "registry-ui",
                  "deploy": {
                    "startCommand": "/bin/sh -c 'sed -i \"s/^worker_processes.*/worker_processes 2;/\" /etc/nginx/nginx.conf; set -- $(grep -m1 ^nameserver /etc/resolv.conf); if [ -z \"$NGINX_RESOLVER\" ] && [ -n \"$2\" ]; then case \"$2\" in *:*) NGINX_RESOLVER=\"[$2] valid=10s\";; *) NGINX_RESOLVER=\"$2 valid=10s\";; esac; fi; export NGINX_RESOLVER; case \"$NGINX_PROXY_PASS_URL\" in \"\"|http://:*) NGINX_PROXY_PASS_URL=\"http://registry.railway.internal:5000\";; esac; export NGINX_PROXY_PASS_URL; echo \"registry-ui: resolver=$NGINX_RESOLVER upstream=$NGINX_PROXY_PASS_URL\"; exec /docker-entrypoint.sh nginx -g \"daemon off;\"'",
                    "healthcheckPath": "/",
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "joxit/docker-registry-ui:latest"
                  },
                  "variables": {
                    "PORT": {
                      "isOptional": false,
                      "description": "HTTP port nginx listens on",
                      "defaultValue": "8080"
                    },
                    "PULL_URL": {
                      "isOptional": false,
                      "description": "Hostname shown in pull commands",
                      "defaultValue": "${{registry.RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "DELETE_IMAGES": {
                      "isOptional": false,
                      "description": "Offer tag deletion in the UI",
                      "defaultValue": "true"
                    },
                    "REGISTRY_TITLE": {
                      "isOptional": false,
                      "description": "Name shown in the UI header",
                      "defaultValue": "Docker Registry"
                    },
                    "SINGLE_REGISTRY": {
                      "isOptional": false,
                      "description": "Hide the registry switcher",
                      "defaultValue": "true"
                    },
                    "REGISTRY_SECURED": {
                      "isOptional": false,
                      "description": "Registry requires authentication",
                      "defaultValue": "true"
                    },
                    "SHOW_TAG_HISTORY": {
                      "isOptional": false,
                      "description": "Show image history and layers",
                      "defaultValue": "true"
                    },
                    "NGINX_LISTEN_PORT": {
                      "isOptional": false,
                      "description": "Same port, read by the image entrypoint",
                      "defaultValue": "8080"
                    },
                    "TAGLIST_PAGE_SIZE": {
                      "isOptional": false,
                      "description": "Tags shown per page",
                      "defaultValue": "100"
                    },
                    "SHOW_CONTENT_DIGEST": {
                      "isOptional": false,
                      "description": "Show manifest digests in tag lists",
                      "defaultValue": "true"
                    },
                    "NGINX_PROXY_PASS_URL": {
                      "isOptional": false,
                      "description": "Private registry upstream",
                      "defaultValue": "http://${{registry.RAILWAY_PRIVATE_DOMAIN}}:5000"
                    },
                    "SHOW_CATALOG_NB_TAGS": {
                      "isOptional": false,
                      "description": "Show tag counts on the catalog",
                      "defaultValue": "true"
                    },
                    "CATALOG_ELEMENTS_LIMIT": {
                      "isOptional": false,
                      "description": "Maximum repositories fetched",
                      "defaultValue": "1000"
                    },
                    "CATALOG_DEFAULT_EXPANDED": {
                      "isOptional": false,
                      "description": "Expand repository groups by default",
                      "defaultValue": "1"
                    }
                  },
                  "networking": {
                    "serviceDomains": {
                      "<hasDomain>:8080": {
                        "port": 8080
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "post_deploy": {
    "healthcheck": {
      "service": "registry-ui",
      "method": "GET",
      "path": "/",
      "expect_status": 200
    }
  },
  "resources": {
    "expected_services": 3,
    "needs_volume": true
  },
  "generated_at": "2026-09-13T16:14:48.926Z",
  "generator_version": "0.1.0",
  "status": "degraded",
  "validated_at": "2026-09-12T17:55:54.702Z",
  "success_rate_30d": 0.3333,
  "validation": {
    "last_run_id": "run_0937b51b111b401084b4",
    "checks": [
      {
        "name": "workflow_completed",
        "passed": false,
        "detail": "Your workspace has been restricted. Please contact support to resolve this."
      }
    ],
    "typical_ready_seconds": 47
  }
}
