Environment Configuration
Query, stage, and apply configuration changes for Railway environments.
Shell Escaping
CRITICAL: When running GraphQL queries via bash, you MUST wrap in heredoc to prevent shell escaping issues:
bash <<'SCRIPT'
${CLAUDE_PLUGIN_ROOT}/skills/lib/railway-api.sh 'query ...' '{"var": "value"}'
SCRIPT
Without the heredoc wrapper, multi-line commands break and exclamation marks in GraphQL non-null types get escaped, causing query failures.
When to Use
- User wants to create a new environment
- User wants to duplicate an environment (e.g., "copy production to staging")
- User wants to switch to a different environment
- User asks about current build/deploy settings, variables, replicas, health checks, domains
- User asks to change service source (Docker image, branch, commit, root directory)
- User wants to connect a service to a GitHub repo
- User wants to deploy from a GitHub repo (create empty service first via railway-new skill, then use this)
- User asks to change build or start command
- User wants to add/update/delete environment variables
- User wants to change replica count or configure health checks
- User asks to delete a service, volume, or bucket
- User says "apply changes", "commit changes", "deploy changes"
- Auto-fixing build errors detected in logs
Create Environment
Create a new environment in the linked project:
railway environment new <name>
Duplicate an existing environment:
railway environment new staging --duplicate production
With service-specific variables:
railway environment new staging --duplicate production --service-variable api PORT=3001
Switch Environment
Link a different environment to the current directory:
railway environment <name>
Or by ID:
railway environment <environment-id>
Get Context
railway status --json
Extract:
project.id - for service lookup
environment.id - for the mutations
service.id - default service if user doesn't specify one
Resolve Service ID
If user specifies a service by name, query project services:
query projectServices($projectId: String!) {
project(id: $projectId) {
services {
edges {
node {
id
name
}
}
}
}
}
Match the service name (case-insensitive) to get the service ID.
Query Configuration
Fetch current environment configuration and staged changes.
query environmentConfig($environmentId: String!) {
environment(id: $environmentId) {
id
config(decryptVariables: false)
serviceInstances {
edges {
node {
id
serviceId
}
}
}
}
environmentStagedChanges(environmentId: $environmentId) {
id
patch(decryptVariables: false)
}
}
Example:
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
Response Structure
The config field contains current configuration:
{
"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": "..." } }
}
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.
Get Rendered Variables
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:
railway variables --json
railway variables --service <service-name> --json
When to use:
- Debugging connection issues (see actual URLs/ports)
- Verifying variable resolution is correct
- Viewing Railway-injected values (RAILWAY_*)
Stage Changes
Stage configuration changes via the environmentStageChanges mutation. Use merge: true to automatically merge with existing staged changes.
mutation stageEnvironmentChanges(
$environmentId: String!
$input: EnvironmentConfig!
$merge: Boolean
) {
environmentStageChanges(
environmentId: $environmentId
input: $input
merge: $merge
) {
id
}
}
Important: Always use variables (not inline input) because service IDs are UUIDs which can't be used as unquoted GraphQL object keys.
Example:
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}'
SCRIPT
Delete Service
Use isDeleted: true:
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": {"isDeleted": true}}}, "merge": true}'
SCRIPT
Stage and Apply Immediately
For single changes that should deploy right away, use environmentPatchCommit to stage and apply in one call.
mutation environmentPatchCommit(
$environmentId: String!
$patch: EnvironmentConfig
$commitMessage: String
) {
environmentPatchCommit(
environmentId: $environmentId
patch: $patch
commitMessage: $commitMessage
)
}
Example:
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"}'
SCRIPT
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".