npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tachyon-gg/railway-deploy

v0.3.1

Published

[![CI](https://github.com/tachyon-gg/railway-deploy/actions/workflows/ci.yml/badge.svg)](https://github.com/tachyon-gg/railway-deploy/actions/workflows/ci.yml) [![Integration Tests](https://github.com/tachyon-gg/railway-deploy/actions/workflows/integratio

Readme

railway-deploy

CI Integration Tests codecov TypeScript Bun Biome

Declarative infrastructure management for Railway. Define your Railway project's services, variables, domains, volumes, and buckets in YAML, and railway-deploy will diff against the live state and apply changes atomically -- like Terraform, but purpose-built for Railway.

Quick start

# Install
npx @tachyon-gg/railway-deploy --help

# Validate a config file
npx @tachyon-gg/railway-deploy --validate project.yaml

# Dry-run (show what would change)
npx @tachyon-gg/railway-deploy project.yaml -e production

# Apply changes
npx @tachyon-gg/railway-deploy --apply -e production project.yaml

CLI flags

| Flag | Description | |------|-------------| | -e, --environment <name> | Target environment (required except for --validate) | | --apply | Execute changes (default: dry-run) | | --stage | Stage changes in Railway without committing (preview in dashboard) | | -y, --yes | Skip confirmation for destructive ops | | --allow-data-loss | Allow operations that can cause data loss (e.g., volume deletion) | | --env-file <path> | Load .env file for ${VAR} resolution | | -v, --verbose | Show detailed diffs (old -> new values) | | --no-color | Disable ANSI color output | | --validate | Validate config without connecting to Railway |

Environment variables

| Variable | Description | |----------|-------------| | RAILWAY_TOKEN | Railway API token (required for all API operations) |


Config reference

Project configs are YAML files describing the desired state of a Railway project across one or more environments. Add schema support to your editor:

# yaml-language-server: $schema=./schemas/project.schema.json

Top-level structure

project: My Project              # Railway project name (must match exactly)
environments:                    # Environments to manage
  - staging
  - production

shared_variables: { ... }        # Variables shared across all services
services: { ... }                # Service definitions
volumes: { ... }                 # Persistent volume definitions
buckets: { ... }                 # S3-compatible bucket definitions

Shared variables

Shared variables are available to all services in an environment. Use the string shorthand for values that are the same everywhere, or the object form for per-environment overrides:

shared_variables:
  # String shorthand — same value in all environments
  ADMIN_PORT: "8081"
  PUBLIC_PORT: "8080"

  # Object form — default value with per-environment overrides
  JWT_SECRET:
    value: ${JWT_SECRET_DEFAULT}
    environments:
      staging:
        value: ${JWT_SECRET_STAGING}
      production:
        value: ${JWT_SECRET_PROD}

Supports ${ENV_VAR} syntax (resolved from your local environment or --env-file) and ${{shared.OTHER_VAR}} self-references.

Note: Shared variables cannot contain ${{service.VAR}} cross-service references. Railway resolves shared variables without a service context.

Volumes

Volumes are declared at the top level with optional per-environment overrides. Services reference them by name.

volumes:
  pg-data:
    size_mb: 50000
    region: us-east4
    environments:
      production:
        size_mb: 100000

  redis-data: {}                 # Minimal declaration — Railway defaults

services:
  postgres:
    source:
      image: postgres:17
    volume:                      # Reference a declared volume
      name: pg-data
      mount: /var/lib/postgresql/data

Every volume referenced by a service must be declared in the volumes block.

Buckets

S3-compatible Railway buckets. The key is the bucket name.

buckets:
  media-uploads:
    region: iad
    environments:
      eu-production:
        region: fra

  logs: {}                       # Minimal — uses default region

Services

Each service defines defaults that apply to all environments. Per-environment overrides go under environments.<name>:

services:
  web:
    source:
      repo: myorg/web-app
    start_command: npm start
    variables:
      PORT: "3000"
    environments:
      staging:
        source:
          repo: myorg/web-app
          branch: develop
      production:
        source:
          repo: myorg/web-app
          branch: main
          wait_for_ci: true

  # Service without environments block — exists in all environments
  redis:
    source:
      image: redis:7

  # Service scoped to specific environments
  debug-tools:
    source:
      image: debug:latest
    environments:
      staging: {}                # Only in staging

Service scope rules

  • Service has environments block -> only exists in environments listed there
  • Service has no environments block -> exists in ALL declared environments

Merge rules

When a service has per-environment overrides:

| Field type | Merge behavior | |------------|---------------| | params, variables | Shallow merge (override keys replace defaults) | | domains, source, volume, regions, healthcheck, build | Replace entirely | | Scalar fields (start_command, etc.) | Override replaces |


Service fields reference

Every field below can be used on service defaults, per-environment overrides, and templates.

Source

Source is a discriminated union — use either repo or image, not both.

# Repo source — deploy from a GitHub repository
source:
  repo: myorg/my-repo
  branch: main                   # Branch to deploy from
  root_directory: /packages/api  # Root directory (monorepo support)
  wait_for_ci: true              # Wait for GitHub Actions to pass before deploying
# Image source — deploy from a container image
source:
  image: nginx:latest            # Docker image (Docker Hub, GHCR, etc.)
  registry_credentials:          # For private container registries
    username: ${REGISTRY_USER}
    password: ${REGISTRY_PASS}
  auto_updates:                  # Auto-update schedule for image-based services
    monday:
      start_hour: 0
      end_hour: 6
    friday:
      start_hour: 0
      end_hour: 6

Build

Build is a discriminated union — fields depend on the builder value.

# Railpack (default)
build:
  builder: railpack
  command: npm run build         # Custom build command
  watch_patterns:                # File patterns that trigger deploys
    - /packages/api/src/**
  metal: true                    # Enable Metal build environment (faster builds)
# Nixpacks
build:
  builder: nixpacks
  command: npm run build
  watch_patterns:
    - /packages/api/src/**
  metal: true
# Dockerfile
build:
  builder: dockerfile
  dockerfile_path: Dockerfile.prod  # Path to Dockerfile
  watch_patterns:
    - /packages/api/src/**
  metal: true

railway_config_file is a separate service-level field (not part of build):

railway_config_file: railway.toml  # Path to railway.json/toml in the repository

Deploy

start_command: npm start         # Custom start command

pre_deploy_command:              # Run before deployment (e.g., migrations)
  - npm run migrate
  - npm run seed

cron_schedule: "*/5 * * * *"     # Cron schedule (5-field format)
                                 # Note: cron forces restart_policy to NEVER
                                 # and disables serverless

healthcheck:                     # HTTP healthcheck
  path: /health
  timeout: 300                   # Timeout in seconds (default: 300)

# Restart policy — string shorthand or object with max_retries
restart_policy: always           # always, never, or on_failure

restart_policy:                  # Object form for on_failure with retries
  type: on_failure
  max_retries: 5

serverless: true                 # Enable serverless sleeping (scale to zero when idle)
draining_seconds: 30             # Graceful shutdown timeout (SIGTERM to SIGKILL)
overlap_seconds: 10              # Blue-green deploy overlap duration

Networking

# Custom domains
domains:
  - app.example.com              # Simple domain
  - domain: api.example.com      # Domain with target port
    target_port: 8080

# Railway-provided domain (*.up.railway.app)
railway_domain:
  target_port: 3000

# TCP proxy (for non-HTTP services like databases)
tcp_proxy: 5432

# Private networking
private_hostname: postgres       # Internal DNS hostname for service-to-service communication

# Outbound networking
ipv6_egress: true                # Enable IPv6 outbound traffic
static_outbound_ips: true        # Assign permanent outbound IP addresses

Scaling

regions: us-east4                # Single region (1 replica)
# or
regions:                         # Multi-region with replica counts
  us-east4: 3
  us-west1: 1

limits:                          # Resource limits per replica
  memory_gb: 8
  vcpus: 4

Storage

volume:                          # Reference a top-level volume
  name: pg-data                  # Must match a key in the volumes block
  mount: /var/lib/postgresql/data # Absolute mount path

Variables

variables:
  PORT: "3000"
  DATABASE_URL: ${{Postgres.DATABASE_URL}}    # Railway runtime reference
  API_KEY: ${LOCAL_API_KEY}                    # Resolved from local env at config time
  OLD_VAR: null                                # Marks for deletion

Variable syntax

| Syntax | Resolved | Description | |--------|----------|-------------| | ${ENV_VAR} | At config load time | Reads from local environment (or --env-file) | | ${{service.VAR}} | At Railway runtime | Railway reference variable (cross-service) | | %{param} | At config load time | Template parameter substitution | | %{service_name} | At config load time | Built-in: the service's config key | | null | N/A | Marks a variable for deletion |

%{param} is expanded first, so it can be used inside ${{}} Railway references:

variables:
  DATABASE_URL: ${{%{service_name}.DATABASE_URL}}
  REDIS_URL: ${{%{cache_service}.REDIS_URL}}

Service templates

Templates extract reusable service definitions with parameterized values. The built-in %{service_name} param resolves to the service's key in the config.

# services/web.yaml
params:
  tag:
    required: true
  replicas:
    default: "1"

source:
  image: ghcr.io/org/app:%{tag}

variables:
  APP_VERSION: "%{tag}"
  SERVICE_NAME: "%{service_name}"
  DATABASE_URL: ${{Postgres.DATABASE_URL}}

domains:
  - "%{service_name}.example.com"

healthcheck:
  path: /health
  timeout: 300

regions: us-east4

Referenced from a project config:

services:
  web:
    template: services/web.yaml
    params:
      replicas: "1"
    environments:
      staging:
        params:
          tag: alpha
      production:
        params:
          tag: v2.0.0
          replicas: "3"
        variables:
          EXTRA: added-by-env
          APP_VERSION: null           # Deletes the template-defined variable
        domains:
          - production.example.com    # Overrides template domains

Complete example

# yaml-language-server: $schema=./schemas/project.schema.json
project: My SaaS App
environments:
  - staging
  - production

shared_variables:
  APP_PORT: "3000"
  SENTRY_DSN:
    value: ${SENTRY_DSN_DEFAULT}
    environments:
      production:
        value: ${SENTRY_DSN_PROD}

volumes:
  pg-data:
    size_mb: 50000
    environments:
      production:
        size_mb: 200000
  redis-data: {}

buckets:
  uploads:
    region: iad

services:
  web:
    source:
      repo: myorg/web-app
      root_directory: /packages/web
    build:
      builder: nixpacks
      command: npm run build
      metal: true
    start_command: npm start
    pre_deploy_command: npm run migrate
    healthcheck:
      path: /health
      timeout: 60
    restart_policy:
      type: on_failure
      max_retries: 5
    serverless: true
    railway_domain:
      target_port: 3000
    variables:
      PORT: "3000"
      DATABASE_URL: ${{Postgres.DATABASE_URL}}
    environments:
      staging:
        source:
          repo: myorg/web-app
          branch: develop
        domains:
          - staging.example.com
      production:
        source:
          repo: myorg/web-app
          branch: main
          wait_for_ci: true
        domains:
          - app.example.com
          - domain: api.example.com
            target_port: 8080
        regions:
          us-east4: 2
        limits:
          memory_gb: 4
          vcpus: 2

  postgres:
    source:
      image: postgres:17
    private_hostname: postgres
    volume:
      name: pg-data
      mount: /var/lib/postgresql/data
    tcp_proxy: 5432
    variables:
      POSTGRES_DB: myapp

  redis:
    source:
      image: redis:7-alpine
    private_hostname: redis
    volume:
      name: redis-data
      mount: /data
    tcp_proxy: 6379

  worker:
    template: services/worker.yaml
    params:
      queue: default
    serverless: false

  cron:
    source:
      repo: myorg/web-app
      root_directory: /packages/cron
    cron_schedule: "0 0 * * *"
    start_command: node scripts/cleanup.js

Known limitations

  • Region management. Setting regions deploys to those regions. Railway always maintains at least one region — the last region cannot be removed. Changing regions is supported (old regions are removed and new ones added). Multi-region is supported via a map of region to replica count.
  • Service groups are read-only. Railway's public API does not expose group creation -- groups can only be managed via the Railway dashboard. Existing groups are respected when reading config.
  • Custom domains may require DNS verification to take effect.
  • Registry credentials are write-only. Railway never returns credentials in config responses, so removal of registry credentials from your config is not detectable -- we simply stop sending them.
  • Static outbound IPs are managed via a separate API call (not atomic with the config patch). If the patch succeeds but the egress call fails, IPs may not be configured.
  • Volume size/region can only be set or increased, not cleared or reduced. Railway does not support shrinking volumes.
  • Volume mount removal is supported via the volumeDelete mutation and requires the --allow-data-loss flag, since it permanently deletes the volume and its data.
  • Bucket deletion is not supported by Railway's API. Buckets that are removed from config will be left in place with a warning.

JSON schemas

Editor support (autocompletion, validation) is available via JSON schemas:

  • schemas/project.schema.json -- project config files
  • schemas/service-template.schema.json -- service template files

Development

bun install              # Install dependencies
bun run test             # Run unit tests
bun run test:integration # Run integration tests (requires RAILWAY_TOKEN)
bun run typecheck        # Type check
bun run lint             # Lint (Biome)
bun run lint:fix         # Auto-fix lint issues
bun run codegen          # Regenerate GraphQL types
bun run build            # Build for distribution