Skip to content

About

Thin Python authoring helpers for Railway Infrastructure as Code

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

16 Commits

Folders and files

Repository files navigation

Railway Infrastructure as Code (IaC) authoring helpers for Python.

Install: pip install railway-sdk (or pip install -e . from this repo) then author .railway/railway.py. Prefer one file per project that owns the whole environment. Named partials are a last resort for split repos that cannot share a file.

from railway_sdk import define_railway, github, postgres, project, service

@define_railway
def main(ctx=None):
    db = postgres("db")
    web = service(
        "web",
        source=github("org/app"),
        start="gunicorn app:app",
        env={"DATABASE_URL": db.env.DATABASE_URL},
        tracing={"enabled": True},
        cdn={"enabled": True},
    )
    return project("my-app", resources=[db, web])

Functions are single-file services run by Railway's function image. Point fn at the file with code_file (relative to the config file) and it reads it when the config is evaluated, compiling it to the image plus the base64 start command that the dashboard editor and railway functions push write — the same thing you would get by hand with source=image(...) and start="./run.sh <base64>". Source is capped at FUNCTION_SOURCE_MAX_BYTES (98295 bytes, just under 96 KB: the whole start command has to fit one argv entry), checked at authoring time before any plan.

from railway_sdk import fn

hook = fn(
    "hook",
    code_file="functions/hook.ts",
    env={"DATABASE_URL": db.env.DATABASE_URL},
    domains=["hook.example.com"],
)
cron = fn("nightly", code_file="functions/nightly.ts", deploy={"cronSchedule": "0 3 * * *"})

runtime pins the image: fn("hook", "hook.ts", runtime="bun:1.2.16"). The default is the current Bun runtime (FUNCTION_IMAGES["bun"]), and an explicit source=image(...) wins over runtime. The pieces are exported on their own too: function_image(runtime) resolves a runtime to its image and function_start_command(code) encodes source into the start command. Without code_file, fn is a plain service node tagged kind: function for config you manage elsewhere.

The CLI evaluates this file and diffs against the linked environment. Config as Code migration lives in the CLI (railway config migrate --lang py).

Image auto updates

Docker image services can update themselves inside weekly UTC maintenance windows. Pass autoUpdates to image() with a policy ("patch", "minor" or "disabled") and a schedule of ScheduleWindow entries. Each window names a UTC weekday (0 is Sunday through 6 is Saturday), the hour it opens (0-23, inclusive) and the hour it closes (1-24, exclusive).

from railway_sdk import ScheduleWindow, image, service

web = service(
    "web",
    source=image(
        "nginx:latest",
        autoUpdates={
            "type": "patch",
            # Saturday 02:00-06:00 UTC and all of Sunday.
            "schedule": [ScheduleWindow(6, 2, 6), ScheduleWindow(day=0, start_hour=0, end_hour=24)],
        },
    ),
)

The windows serialise to Railway's source.autoUpdates.schedule shape ({"day": 6, "startHour": 2, "endHour": 6}) and are validated when the file is evaluated, so an out-of-range day or a window that ends before it starts fails locally rather than at apply. Updates only ever run inside a window, so a policy with no schedule relies on one already set in the dashboard and never fires otherwise. Auto updates only apply to Docker Hub and GHCR images; image() raises for any other registry, and github() rejects the option.

CDN

CDN caching is set per service and per environment, for the service's public domains. The settings live on the edge and apply without a deploy. Pass cdn to service() or fn() as a camelCase mapping or a ServiceCdn.

from railway_sdk import ServiceCdn, service

web = service("web", cdn={"enabled": True})

docs = service(
    "docs",
    cdn=ServiceCdn(
        enabled=True,
        default_ttl_seconds=86400,
        html_caching="force",
        stale_while_revalidate=False,
        purge_on_deploy="all",
    ),
)

enabled caches responses from the service's public domains at Railway's edge. It needs a public domain and cannot be combined with Railway Authentication. The other knobs tune the cache and each has a default:

  • defaultTtlSeconds (default_ttl_seconds): how long the edge keeps a cacheable response without Cache-Control headers of its own, in seconds. Defaults to 7200.
  • htmlCaching (html_caching): whether HTML documents are cached. "auto" follows the response headers, "force" caches them regardless, "never" leaves them uncached. Defaults to "auto".
  • staleWhileRevalidate (stale_while_revalidate): honour stale-while-revalidate Cache-Control directives, serving a stale copy while the edge refreshes it. Defaults to True.
  • purgeOnDeploy (purge_on_deploy): what a successful deploy purges from the cache. "html" keeps immutable assets warm, "all" clears everything, "off" purges nothing. Defaults to "html".

Railway serialises the block only while caching is on, and only the knobs that differ from the defaults, so the SDK passes it through as written. Leaving the block out, {"enabled": False} and None all mean the same thing: no caching, and no drift in railway config plan. {"enabled": True} is a service caching with the defaults, so a knob set to its default does not plan as drift either. Unknown keys, wrong types and unknown htmlCaching/purgeOnDeploy values fail when the file is evaluated.

Requires a Railway CLI that compiles cdn. Older CLIs drop the block when compiling the config.

Last resort only: set module-level PARTIAL = "api" (same role as export const partial in TypeScript). Do not rename a partial after apply.

See https://docs.railway.com/infrastructure-as-code

About

Thin Python authoring helpers for Railway Infrastructure as Code

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages