npx skills add ...
npx skills add davila7/claude-code-templates --skill railway-environment
Query, stage, and apply configuration changes for Railway environments. Use for ANY variable or env var operations, service configuration (source, build settings, deploy settings), lifecycle (delete service), and applying changes. Prefer over railway-status skill for any configuration or variable queries.
npx skills add davila7/claude-code-templates --skill railway-environment
Query, stage, and apply configuration changes for Railway environments.
CRITICAL: When running GraphQL queries via bash, you MUST wrap in heredoc to prevent shell escaping issues:
Without the heredoc wrapper, multi-line commands break and exclamation marks in GraphQL non-null types get escaped, causing query failures.
Create a new environment in the linked project:
Duplicate an existing environment:
With service-specific variables:
Link a different environment to the current directory:
Or by ID:
Extract:
project.id - for service lookupenvironment.id - for the mutationsservice.id - default service if user doesn't specify oneIf user specifies a service by name, query project services:
Match the service name (case-insensitive) to get the service ID.
Fetch current environment configuration and staged changes.
Example:
The config field contains current configuration:
The patch field in environmentStagedChanges contains pending changes. The effective configuration is the base config merged with the staged patch.
For complete field reference, see reference/environment-config.md.
For variable syntax and service wiring patterns, see reference/variables.md.
The GraphQL queries above return unrendered variables - template syntax like ${{shared.DOMAIN}} is preserved. This is correct for management/editing.
To see rendered (resolved) values as they appear at runtime:
When to use:
Stage configuration changes via the environmentStageChanges mutation. Use merge: true to automatically merge with existing staged changes.
Important: Always use variables (not inline input) because service IDs are UUIDs which can't be used as unquoted GraphQL object keys.
Example:
Use isDeleted: true:
For single changes that should deploy right away, use environmentPatchCommit to stage and apply in one call.
Example:
When to use: Single change, no need to batch, user wants immediate deployment.
When NOT to use: Multiple related changes to batch, or user says "stage only" / "don't deploy yet".
Commit staged changes and trigger deployments.
Note: There is no railway apply CLI command. Use the mutation below or direct users to the web UI.
Mutation name: environmentPatchCommitStaged
Example:
| Field | Type | Default | Description |
|---|---|---|---|
environmentId | String! | - | Environment ID from status |
message | String | null | Short description of changes |
skipDeploys | Boolean | false | Skip deploys (only if user explicitly asks) |
Keep very short - one sentence max. Examples:
Leave empty if no meaningful description.
Always deploy unless user explicitly asks to skip. Only set skipDeploys: true if user says "apply without deploying", "commit but don't deploy", or "skip deploys".
Returns a workflow ID (string) on success.
By default, apply changes immediately.
Single change: Use environmentPatchCommit to stage and apply in one call.
Multiple changes or batching: Use environmentStageChanges with merge: true for each change, then environmentPatchCommitStaged to apply.
When you don't auto-apply, tell the user:
Changes staged. Apply them at: https://railway.com/project/{projectId} Or ask me to apply them.
Get projectId from railway status --json → project.id
There are no staged changes to commit. Stage changes first.
Common issues:
buildCommand and startCommand cannot be identicalbuildCommand only valid with NIXPACKS builderdockerfilePath only valid with DOCKERFILE builderrailway environment new staging --duplicate productionrailway environment new staging --duplicate production --service-variable api PORT=3001railway environment <name>railway environment <environment-id>railway status --jsonquery projectServices($projectId: String!) {
project(id: $projectId) {
services {
edges {
node {
id
name
}
}
}
}
}query environmentConfig($environmentId: String!) {
environment(id: $environmentId) {
id
config(decryptVariables: false)
serviceInstances {
edges {
node {
id
serviceId
}
}
}
}
environmentStagedChanges(environmentId: $environmentId) {
id
patch(decryptVariables: false)
}
}bash <<'SCRIPT'
${CLAUDE_PLUGIN_ROOT}/skills/lib/railway-api.sh \
'query envConfig($envId: String!) {
environment(id: $envId) { id config(decryptVariables: false) }
environmentStagedChanges(environmentId: $envId) { id patch(decryptVariables: false) }
}' \
'{"envId": "ENV_ID"}'
SCRIPT{
"services": {
"<serviceId>": {
"source": { "repo": "...", "branch": "main" },
"build": { "buildCommand": "npm run build", "builder": "NIXPACKS" },
"deploy": {
"startCommand": "npm start",
"multiRegionConfig": { "us-west2": { "numReplicas": 1 } }
},
"variables": { "NODE_ENV": { "value": "production" } },
"networking": { "serviceDomains": {}, "customDomains": {} }
}
},
"sharedVariables": { "DATABASE_URL": { "value": "..." } }
}# Current linked service
railway variables --json
# Specific service
railway variables --service <service-name> --jsonmutation stageEnvironmentChanges(
$environmentId: String!
$input: EnvironmentConfig!
$merge: Boolean
) {
environmentStageChanges(
environmentId: $environmentId
input: $input
merge: $merge
) {
id
}
}bash <<'SCRIPT'
${CLAUDE_PLUGIN_ROOT}/skills/lib/railway-api.sh \
'mutation stageChanges($environmentId: String!, $input: EnvironmentConfig!, $merge: Boolean) {
environmentStageChanges(environmentId: $environmentId, input: $input, merge: $merge) { id }
}' \
'{"environmentId": "ENV_ID", "input": {"services": {"SERVICE_ID": {"build": {"buildCommand": "npm run build"}}}}, "merge": true}'
SCRIPTbash <<'SCRIPT'
${CLAUDE_PLUGIN_ROOT}/skills/lib/railway-api.sh \
'mutation stageChanges($environmentId: String!, $input: EnvironmentConfig!, $merge: Boolean) {
environmentStageChanges(environmentId: $environmentId, input: $input, merge: $merge) { id }
}' \
'{"environmentId": "ENV_ID", "input": {"services": {"SERVICE_ID": {"isDeleted": true}}}, "merge": true}'
SCRIPTmutation environmentPatchCommit(
$environmentId: String!
$patch: EnvironmentConfig
$commitMessage: String
) {
environmentPatchCommit(
environmentId: $environmentId
patch: $patch
commitMessage: $commitMessage
)
}bash <<'SCRIPT'
${CLAUDE_PLUGIN_ROOT}/skills/lib/railway-api.sh \
'mutation patchCommit($environmentId: String!, $patch: EnvironmentConfig, $commitMessage: String) {
environmentPatchCommit(environmentId: $environmentId, patch: $patch, commitMessage: $commitMessage)
}' \
'{"environmentId": "ENV_ID", "patch": {"services": {"SERVICE_ID": {"variables": {"API_KEY": {"value": "secret"}}}}}, "commitMessage": "add API_KEY"}'
SCRIPTmutation environmentPatchCommitStaged(
$environmentId: String!
$message: String
$skipDeploys: Boolean
) {
environmentPatchCommitStaged(
environmentId: $environmentId
commitMessage: $message
skipDeploys: $skipDeploys
)
}bash <<'SCRIPT'
${CLAUDE_PLUGIN_ROOT}/skills/lib/railway-api.sh \
'mutation commitStaged($environmentId: String!, $message: String) {
environmentPatchCommitStaged(environmentId: $environmentId, commitMessage: $message)
}' \
'{"environmentId": "ENV_ID", "message": "add API_KEY variable"}'
SCRIPTService "foo" not found in project. Available services: api, web, workerNo patch to applyYou don't have permission to modify this environment. Check your Railway role.No project linked. Run `railway link` to link a project.