{
  "manifest_version": "1.0.0",
  "template": {
    "id": "14381b4c-eb22-4c7e-b63d-aa76fad57b15",
    "slug": "nginx-or-just-updated-static-site-host-w",
    "name": "Nginx | (Just Updated) Static Site Host with Token Upload API, Reverse-Proxy Ready, Files Survive Redeploys",
    "description": "Nginx static site host: upload files over HTTP with a token, on a volume",
    "url": "https://railway.com/deploy/nginx-or-just-updated-static-site-host-w",
    "upstream": {
      "image": "nginx:1.30.6@sha256:42c68eab6f7e22611786a9fe1d1c019e275c50185bd54b8f75dc2b9184322703"
    }
  },
  "status": "unvalidated",
  "validated_at": null,
  "success_rate_30d": null,
  "services": [
    {
      "name": "nginx",
      "source": {
        "image": "nginx:1.30.6@sha256:42c68eab6f7e22611786a9fe1d1c019e275c50185bd54b8f75dc2b9184322703"
      },
      "needs_volume": true,
      "volume_mount_path": "/data",
      "http": true
    }
  ],
  "required_inputs": [
    {
      "key": "SITE_URL",
      "service": "nginx",
      "description": "Provide a value for SITE_URL.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "UPLOAD_TOKEN",
      "service": "nginx",
      "description": "Provide a value for UPLOAD_TOKEN.",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "SITE_PRIVATE_URL",
      "service": "nginx",
      "description": "Provide a value for SITE_PRIVATE_URL.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    }
  ],
  "deploy": {
    "mcp": {
      "server": "railway",
      "tool": "deploy_template",
      "args": {
        "template_code": "nginx-or-just-updated-static-site-host-w"
      }
    },
    "cli": "railway deploy --template nginx-or-just-updated-static-site-host-w",
    "api": {
      "method": "POST",
      "path": "/graphql/v2",
      "body": {
        "query": "mutation templateDeploy($input: TemplateDeployV2Input!) { templateDeployV2(input: $input) { projectId workflowId } }",
        "variables": {
          "input": {
            "templateId": "14381b4c-eb22-4c7e-b63d-aa76fad57b15",
            "serializedConfig": {
              "buckets": {},
              "services": {
                "a400839f-8980-4eed-b1a6-cd7a1c7670c2": {
                  "icon": null,
                  "name": "nginx",
                  "deploy": {
                    "startCommand": "/bin/sh -c 'set -eu\n# Boot script for the Railway template. Runs as root inside the stock nginx image (a start command replaces the image ENTRYPOINT).\numask 022\nD=/data\nROOT=\"$D/www\"\nPORT=\"${PORT:-8080}\"\n: \"${UPLOAD_TOKEN:?UPLOAD_TOKEN is not set}\"\nmkdir -p \"$ROOT\" \"$D/tmp\" \"$D/nginx.d\"\nif [ ! -e \"$ROOT/index.html\" ]; then\ncat > \"$ROOT/index.html\" <<\"HTML\"\n<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><title>Nginx on Railway</title></head>\n<body><h1>Nginx is running</h1>\n<p>Upload your site with <code>curl -u deploy:$UPLOAD_TOKEN -T index.html $SITE_URL/index.html</code>.</p></body></html>\nHTML\nfi\nchown -R nginx:nginx \"$D/www\" \"$D/tmp\" \"$D/nginx.d\"\n# The upload credential is the user deploy plus UPLOAD_TOKEN; the hash is rebuilt on every boot, so rotating the variable rotates the credential.\nprintf \"deploy:%s\\n\" \"$(openssl passwd -apr1 \"$UPLOAD_TOKEN\")\" > /etc/nginx/upload.htpasswd\nchgrp nginx /etc/nginx/upload.htpasswd\nchmod 640 /etc/nginx/upload.htpasswd\ncat > /etc/nginx/conf.d/default.conf <<\"CONF\"\nserver {\n    listen __PORT__ default_server;\n    listen [::]:__PORT__ default_server;\n    server_name _;\n    root /data/www;\n    index index.html;\n    server_tokens off;\n    client_max_body_size 512m;\n    # Uploads are written to a temp file and renamed into place, so the temp dir must sit on the same volume as the site.\n    client_body_temp_path /data/tmp;\n    gzip on;\n    gzip_types text/plain text/css application/javascript application/json image/svg+xml;\n    location = /healthz {\n        access_log off;\n        default_type text/plain;\n        return 200 \"ok\\n\";\n    }\n    location / {\n        limit_except GET HEAD {\n            auth_basic \"upload\";\n            auth_basic_user_file /etc/nginx/upload.htpasswd;\n        }\n        dav_methods PUT DELETE MKCOL COPY MOVE;\n        dav_access user:rw group:rw all:r;\n        create_full_put_path on;\n    }\n    include /data/nginx.d/*.conf;\n}\nCONF\nsed -i \"s/__PORT__/$PORT/g\" /etc/nginx/conf.d/default.conf\nnginx -t\nexec nginx -g \"daemon off;\"'",
                    "healthcheckPath": "/healthz",
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "nginx:1.30.6@sha256:42c68eab6f7e22611786a9fe1d1c019e275c50185bd54b8f75dc2b9184322703"
                  },
                  "variables": {
                    "SITE_URL": {
                      "isOptional": false,
                      "defaultValue": "https://${{RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "UPLOAD_TOKEN": {
                      "isOptional": false,
                      "defaultValue": "{{UPLOAD_TOKEN}}"
                    },
                    "SITE_PRIVATE_URL": {
                      "isOptional": false,
                      "defaultValue": "http://${{RAILWAY_PRIVATE_DOMAIN}}:8080"
                    }
                  },
                  "networking": {
                    "serviceDomains": {
                      "<hasDomain>:8080": {
                        "port": 8080
                      }
                    }
                  },
                  "volumeMounts": {
                    "a400839f-8980-4eed-b1a6-cd7a1c7670c2": {
                      "mountPath": "/data"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "post_deploy": {
    "healthcheck": {
      "service": "nginx",
      "method": "GET",
      "path": "/healthz",
      "expect_status": 200
    }
  },
  "resources": {
    "expected_services": 1,
    "needs_volume": true
  },
  "generated_at": "2026-11-06T04:15:09.163Z",
  "generator_version": "0.1.0"
}
