Blog

Notes from building a deployment platform.

Mechanics we had to get right, decisions we had to defend, and migrations we have run more than once.

One app plus Postgres: what it actually costs, platform by platform

The published prices for running a small app with a database, compared directly — including what you're actually renting versus what you're renting plus a markup.

Letting an AI agent deploy your app, safely

A scoped MCP server for Claude Code, Cursor, and any other MCP client: what it can do on its own, what it can't, and why destructive actions wait for a human.

Postgres automatic failover, self-hosted

What a live Postgres standby promotion actually looks like on your own servers: the 3-minute detection window, what gets copied, what doesn't, and how it compares to a managed database.

Self-hosted Plausible analytics, in one command

We had no idea who was actually reading railyard.run. Here's the one-click Plausible stack we launched on our own platform to fix that, and what it actually took.

What happens when your AI-built app reaches production

An agent can write a working app in an afternoon. It doesn't know your secrets shouldn't sit in plaintext, or that a deploy token shouldn't be able to delete things. The platform underneath has to know that instead.

Deploying a Next.js app without a Vercel account

Next.js detects and builds itself on Railyard the same way it does anywhere else — the difference is the server underneath is yours, not a per-request bill.

Canary deploys that actually watch error rate, not just a timer

Send a new release 5% of traffic, watch it against the rest of the app, and let it roll itself back if the 5xx rate climbs — on servers you own, not a managed platform's canary toggle.

The self-hosted PaaS timeline, and where the idea actually came from

Cloud 66 proved 'never host the customer's app' in 2012. Here's the real lineage from Heroku through Fly, Render, Railway and Coolify — and why the idea keeps getting rebuilt instead of settling on one winner.

A status page that isn't a $29/month subscription

90-day uptime bars, incidents that open and close on their own, a JSON feed for your own tooling — built from the same uptime checks the platform already runs, not a separate product to pay for.

Deploying Phoenix when your job queue is already inside the release

Oban runs in the same OTP supervision tree as the web endpoint — no second worker process to declare just to run background jobs.

A WAF and rate limiting without a separate edge vendor

OWASP Core Rule Set filtering, per-path rate limits, country blocking — configured on the app, enforced before the request reaches it. Here's exactly what's live today and what's still rolling out.

Deploying Laravel without hand-writing a queue worker config

APP_KEY, migrations, Horizon, Reverb, Octane — Railyard reads what's already in composer.json and .env.example instead of asking you to declare it twice.

A stable outbound IP, without a NAT gateway bill

Putting your server's address on a third-party API's allowlist usually means standing up a NAT gateway on a multi-tenant platform. On a server you own, the IP was never shared to begin with.

Deploying a Go service: the build that doesn't need a runtime

A Go binary doesn't need an interpreter fetched, a runtime version resolved, or anything installed at deploy time — which makes it one of the simplest things to build correctly on a shared build server.

Deploying Django without writing a Procfile by hand

collectstatic, gunicorn, migrations, Celery — Railyard detects what's already in requirements.txt instead of asking you to declare the same information twice.

Running your own ChatGPT-style interface, self-hosted

LibreChat connects to whichever model providers you give it keys for, with your conversations in a database you control. Two real gotchas we hit getting it verified, and how the template avoids them for you.

Deploying Spring Boot: why a Dockerfile is the right answer, not a fallback

JVM build and run commands vary too much by project layout to guess safely — which is exactly why a Dockerfile is the deliberate, correct way to deploy a Spring Boot app, not a workaround for missing support.

Deploying Flask: a micro-framework needs micro-configuration, not none

Flask doesn't assume a migration tool or a job queue the way Django does — which means a Flask deploy is honest about what's actually there instead of guessing a convention that doesn't exist.

Running your own Mastodon instance, verified end to end

Built straight from Mastodon's own Dockerfile, Postgres and Redis wired up automatically, encryption secrets generated correctly the first time — not a simplified fork of the real thing.

Running Laravel on your own DigitalOcean droplet, without Forge

Forge configures a DigitalOcean droplet for you, for a monthly fee, on top of the droplet's own cost. Here's what actually has to happen on that droplet — and how to get it without the extra bill.

A deploy button for your README, pointed at servers people own

The one-click deploy badge pattern, but landing on the visitor's own DigitalOcean or Hetzner account instead of a managed platform's metered billing.

A service reference that never breaks, even before you turn on private networking

<name>.railyard.internal resolves differently depending on where the two apps actually run — same container network, public HTTPS, or a cross-provider WireGuard mesh — without the app ever knowing which case applies.

Why a deploy actually fails, reason by reason

A build timeout isn't the same problem as the app answering on 127.0.0.1 instead of 0.0.0.0 — the real causes behind a failed deploy, and the specific fix for each.

railyard.yml: your app's config as one file in git

Pull an app's current configuration as a file, validate it offline, apply it back — exactly what the three commands do today, and what they don't do yet.

A preview environment per pull request, on servers you own

Every PR gets its own running copy, torn down automatically when it closes — and unlike most preview systems, it sleeps when nobody's looking at it instead of billing 24/7.

Deploy Spring Boot to Hetzner

Package a Spring Boot app as a layered Docker image, add Postgres and Caddy HTTPS, and run it on a Hetzner server with backups and a release step.

Finding the cloud waste nobody's looking for

Unattached disks, idle load balancers, servers running at 4% CPU — a scan of your actual cloud accounts, with a one-click fix for most of what it finds, not just a dashboard telling you to go look yourself.

Deploy SvelteKit to Hetzner

Build a SvelteKit app with adapter-node, containerize it, and serve it behind Caddy with HTTPS on a Hetzner Cloud server you control.

When a secret has to be a file, not an environment variable

A GCP service account JSON or a client certificate doesn't fit in a KEY=VALUE pair. Most PaaS env var screens have no good answer for that — here's the one that does.

Self-host MediaCMS for video hosting

Run MediaCMS with Postgres, Redis and a separate Celery worker for transcoding in Docker Compose, behind Caddy HTTPS, with backups.

SSO and an audit log, without an enterprise sales call

Single sign-on, SCIM provisioning and a real audit log aren't features a 6-person team should have to negotiate a contract to get.

Deploy Nuxt to Hetzner

Build a Nuxt app with its Nitro node-server output, containerize it, and serve it behind Caddy with HTTPS on a Hetzner Cloud server.

Why a custom domain doesn't start routing until you've proven you own it

Subdomain takeover is a real, well-documented vulnerability class — pointing a DNS record at infrastructure you don't actually control. Here's the specific check that closes it off by default.

10 SaaS subscriptions you can replace with one-click self-hosted apps

Real open-source projects, in the same category as the SaaS tool next to them, each launchable from Railyard's template catalog in one click — not a feature-by-feature promise, a working app you can actually try.

Self-host Castopod to publish a podcast

Run Castopod with MariaDB and Redis in Docker Compose behind Caddy HTTPS, publish an RSS feed you own, and back up the audio volume.

Connecting TablePlus or psql to a database that has no public port

Managed databases are private by design — only apps on the server can reach them. Here's how a GUI client on your laptop connects anyway, without opening the database to the internet to do it.

Moving a Rails app off Heroku

A practical migration path from a managed platform to servers you own: buildpacks first, then a Dockerfile, addons and the release phase.

Self-host a Mastodon server

Run a real Mastodon instance with Postgres, Redis, the streaming API and Sidekiq as separate services, behind Caddy HTTPS, with backups.

Deploy an Express app to DigitalOcean

Run an Express 5 app with a Postgres-backed route on a DigitalOcean droplet, behind Caddy for HTTPS, plus the shortcut that skips the setup.

Getting paged in Slack before a customer notices, not after

Deploy failures, crash loops, a disk filling up, a certificate about to expire — one rules engine, three channels, scoped per app or across the whole team.

Self-host LibreChat for your own ChatGPT-style app

Run LibreChat with MongoDB in Docker Compose behind Caddy HTTPS, with real secrets generated, registration locked down and backups in place.

Deploy a Go web app to Hetzner

Build and ship a Go net/http service with Postgres on a Hetzner server using Docker, Compose and Caddy, with backups and a repeatable upgrade path.

Workflow automation without the per-execution bill

n8n does what Zapier and Make do — connect APIs without writing glue code — except you own the server and nobody's counting how many times a workflow ran this month.

Self-host AFFiNE, an open-source Notion alternative

Run AFFiNE, the open-source Notion and Miro alternative, with Postgres and Redis in Docker Compose, Caddy HTTPS, backups and an upgrade path.

A deploy isn't done when the container starts

A container being ready and your app actually being reachable are two different things. Railyard checks both, and rolls back if the new version never goes live.

Move a Django app from Heroku to DigitalOcean

Migrate a production Django app off Heroku to a DigitalOcean droplet: Dockerize it, pull the Postgres data across, map config vars, and cut DNS over.

Self-host Meilisearch for fast search

Run Meilisearch in Docker Compose behind Caddy HTTPS, with a real master key, a backup snapshot and an upgrade path you can repeat.

Run Sidekiq or Celery workers next to your web app

Set up a dedicated Sidekiq or Celery worker process alongside your web app, with Docker Compose, systemd, and real failure modes covered.

Where Railyard actually wins against Heroku, Railway, Render and Fly — and where it doesn't

An honest look at the real comparison matrix: four things that follow directly from owning the server, and three things where a competitor is equal or ahead.

Deploy Phoenix to Hetzner

Build a Phoenix release and run it in production on a Hetzner Cloud server, with Postgres and Caddy for HTTPS, all in Docker with mix release.

Database addon or database server: the difference that actually matters

Both give you Postgres. Only one of them survives losing the machine it runs on. Here's exactly where the line is, and when to move.

Migrating Heroku Postgres without a maintenance window

The database is the part of a Heroku migration teams actually worry about. Here's exactly how the data moves, and why there's no real downtime window to schedule.

Self-host changedetection.io to watch web pages

Run changedetection.io on a $6/month server to get notified when any web page changes: price drops, restocks, or a notice page updating.

Move a Rails app from Heroku to Hetzner, step by step

Migrate a production Rails app off Heroku to Hetzner: Dockerize it, pull the Postgres data across, map config vars, and cut DNS over safely.

Deploy Flask to DigitalOcean

Run a Flask app in production on a DigitalOcean Droplet: gunicorn, Postgres and Flask-Migrate behind Caddy for HTTPS, all in Docker.

Self-host Healthchecks.io to monitor cron jobs

Run Healthchecks.io on your own server with Docker, Postgres and HTTPS: the full Compose setup, SMTP alerts, backups and the one-click shortcut.

Replace Heroku review apps with pull request previews

Build your own per-PR preview environments with GitHub Actions and Docker, then see what a hosted version with sleep-on-idle looks like.

Self-host Memos for notes

Run Memos, a lightweight self-hosted note-taking app, on your own $6/month droplet with HTTPS, backups and an upgrade path.

Run cron jobs for Dockerized apps

Add scheduled jobs to a containerized app with a dedicated scheduler container and Docker labels, no cron daemon baked into your image.

Deploy FastAPI to Hetzner

Run a FastAPI app in production on a Hetzner Cloud server: Postgres, Alembic migrations and Caddy for HTTPS, all in Docker.

A Cloud 66 alternative that stays out of your way

Cloud 66 proved the bring-your-own-server model years ago. Here's the lighter, cheaper take on the same idea, and exactly what moving over looks like.

Self-host Rocket.Chat for team chat

Run Rocket.Chat on your own Hetzner server with a MongoDB replica set, HTTPS, backups and an upgrade path, start to finish.

Move a Next.js app from Vercel to your own server

Take a Next.js app off Vercel and onto a server you own: environment variables, a standalone Docker build, HTTPS, cron jobs and a safe DNS cutover.

Deploy Laravel to Hetzner

Run Laravel in production on a Hetzner Cloud server with FrankenPHP, MySQL, a queue worker and the scheduler in Docker, with HTTPS set up automatically.

What actually breaks moving off Render

Render's developer experience is genuinely good. Here's what survives the move to a server you own, and the handful of things that don't carry over automatically.

Self-host Metabase with Postgres

Run Metabase, the open-source BI tool, on your own server with Docker, its own Postgres metadata database, and HTTPS through Caddy, no per-seat license.

Replace Heroku Scheduler with cron jobs on your server

Heroku Scheduler's jobs aren't guaranteed to run on time or at all. Here's how to replace them with cron on your own server, with locking and real logs.

Self-host Jitsi Meet for video calls

Run Jitsi Meet, the open-source video conferencing platform, on your own Docker server with HTTPS, and the one port that trips up most setups.

HTTPS for many apps on one server

Run several small apps on one VPS behind a single Caddy instance, each on its own subdomain with an automatic certificate, plus the automated version.

Deploy Next.js to DigitalOcean

Run a production Next.js app on a DigitalOcean droplet: a standalone Docker build, Caddy for automatic HTTPS, and a repeatable way to ship updates.

Self-host Immich for your photo library

Run Immich, the open-source Google Photos alternative, on your own server with Docker, Postgres and HTTPS through Caddy, and point your phone at it.

The twelve factors, on a server you own

Heroku's 1.0 team wrote the twelve-factor manifesto to describe what their platform demanded of an app. Here's the same twelve factors, mapped to how they actually hold on infrastructure you own instead of rent.

Move off Heroku Key-Value Store (Redis)

Heroku renamed its Redis add-on to Key-Value Store. Here's how to run your own Redis instead, move the data over, and keep your app working.

Deploy Next.js to Hetzner

Build a Next.js app in standalone output mode, containerize it, and serve it behind Caddy with HTTPS on a Hetzner Cloud server.

Self-host Gitea for your own Git server

Run Gitea on your own server with Docker Compose, HTTPS through Caddy and Git over SSH, plus a backup you have tested by restoring it.

Move from Fly.io to your own server

Export your app's image, secrets and Postgres data from Fly and run them on a server you own, with Docker Compose and Caddy.

Self-host Umami analytics

Run Umami on your own server with Docker Compose, Postgres and automatic HTTPS through Caddy, with backups you have restored at least once.

Automatic Postgres backups you can actually restore

Script pg_dump, schedule it with cron, ship it off-server, prune old copies, and prove every backup restores cleanly before you need it.

Deploy Django with Celery to Hetzner

Run Django, gunicorn, Celery and Postgres on a Hetzner server with Docker Compose, Caddy and HTTPS, from scratch to production.

Self-host Vaultwarden, the Bitwarden-compatible password manager

Run your own Bitwarden-compatible vault with Vaultwarden, Docker Compose and Caddy, with a hashed admin token and a working backup.

Rails autoscaling on CPU and response time, not guesswork

Set a floor and a ceiling, and let real load decide — plus how to pre-warm replicas ahead of a traffic spike you can actually predict.

Move from Railway to your own server

Export your Railway app and its Postgres database, stand them up on your own VPS behind Caddy, and the one-click import that does most of it for you.

Deploy Django to DigitalOcean

Run a Django app on a DigitalOcean droplet with Postgres, gunicorn and Caddy for HTTPS, plus the one-click path that skips the setup.

Cron jobs and background workers without a separate dyno

A scheduled command and a background worker are both just processes of your existing app — not a second thing to deploy, scale, or pay for separately.

Self-host Ghost with MySQL

Run Ghost on your own server with MySQL, Docker Compose and Caddy, including real backups, HTTPS, and a tested upgrade path.

Move from Render to your own server

A full walkthrough for moving a Render web service and its Postgres database to a server you own, with HTTPS and backups set up at the end.

Self-host Uptime Kuma for status pages and alerts

Run Uptime Kuma on your own server with Docker Compose and Caddy HTTPS, add backups and upgrades, and see the one-click path too.

Secure a new Ubuntu server before you put apps on it

Lock down a fresh Ubuntu 24.04 server before deploying: a sudo user, key-only SSH, ufw, fail2ban and automatic security updates, step by step.

Deploy a Rails app to DigitalOcean

A full walkthrough: deploy a Dockerized Rails app with Postgres and HTTPS on a $12/month DigitalOcean droplet, with backups and upgrades.

Self-host Chatwoot with Postgres, Redis and Sidekiq

Run your own Chatwoot support inbox on a VPS you control, with Postgres, Redis, a Sidekiq worker and HTTPS, from a bare server to a working login.

OpenTelemetry tracing without touching your infrastructure

Point your app at any OTLP collector and get traces, metrics and logs flowing — the platform sets the environment variables, your app's SDK does the rest.

Move off Heroku Postgres without losing data

Capture a Heroku Postgres backup, restore it into a new database, verify every row, and cut over without downtime or data loss.

Deploy a Rails app to Hetzner

Deploy a Rails app to a Hetzner server with Postgres, Caddy HTTPS and nightly backups using Docker Compose, plus a repeatable release process.

Self-host n8n on a $6 server

Run n8n on your own $6/month droplet with Postgres and HTTPS: the full Docker Compose setup, backups, upgrades and the one-click shortcut.

What makes a deploy actually zero-downtime

Not marketing copy — the specific mechanical steps that mean there's never a moment your app has no working container answering a request.

Deploying Rails without writing a Dockerfile

Most Rails apps need zero container config to deploy. Here's what actually picks the build method, and when you do need to step in.

Deploy your first app on a server you own.

Attach a machine with a host, a user and an SSH key, or let Railyard create one in your cloud account. It installs the rest and watches it come online.

Free plan, no card, and 14 days of Pro for every new team. One flat price per team.