Deploy
You can deploy your Ponder app to any cloud environment that supports Node.js.
Railway
Railway's general-purpose cloud platform is a great starting point for most Ponder apps.
Log in to Railway
Connect your GitHub account, and make sure that your Ponder app has been pushed to remote.
Create a Ponder app service
From the Railway console:
- Click New Project → Deploy from GitHub repo and select your repo from the list
- Click Add variables, then add RPC URLs (e.g. PONDER_RPC_URL_1) and other environment variables
- Create a public domain. In Settings → Networking, click Generate Domain
- Set the healthcheck path and timeout. In Settings → Deploy, set the Healthcheck Path to /readyand the Healthcheck Timeout to86400seconds (1 day)
Monorepo users: Configure the Root Directory and Start Command such
that ponder start runs at the Ponder project root. For example, set the root
directory to packages/ponder or set the start command to cd packages/ponder   && pnpm start.
Create a Postgres database
From the new project dashboard:
- Click Create → Database → Add PostgreSQL
- Open the Variables tab for the Ponder app service, click New Variable → Add Reference → select DATABASE_URLand click Add
After a moment, the Ponder app service should redeploy successfully. Check the Build Logs and Deploy Logs tabs to debug any issues.
Self hosting
In general, hosting a Ponder app is similar to hosting a normal Node.js HTTP server. Rather than offer a step-by-step guide, this section describes the key Ponder-specific quirks to consider when self-hosting.
Health checks & probes
Use the /health and /ready endpoints to configure health checks or probes.
- /health: Returns an HTTP- 200response immediately after the process starts.
- /ready: Returns an HTTP- 200response once indexing progress has reached realtime across all chains. During the historical backfill, the endpoint returns an HTTP- 503response.
Database connection
Your app will have performance issues if the roundtrip database latency exceeds ~20 milliseconds. This is common when using a database in different private network or region.
In production, Ponder works best with a Postgres database in the same private network. Set the DATABASE_URL environment variable to the connection string of your Postgres database, or manually override the database.connectionString option in ponder.config.ts.
import { createConfig } from "ponder";
 
export default createConfig({
  database: {
    kind: "postgres",
    connectionString: "postgres://user:password@mycloud.internal:5432/database",
  },
  // ... more config
});Table names and live views
When a Ponder app starts up, it creates a table in the database for each table exported from ponder.schema.ts. To avoid naming conflicts with prior instances of the same app, the tables are prefixed with a random four-character instance_id.
When the app finishes the historical backfill (or immediately after startup when using ponder dev) it creates a view for each table in ponder.schema.ts using this command.
CREATE VIEW {table_name} AS ( SELECT * FROM {instance_id}__{table_name} );The live view mechanism is essential for zero-downtime deployments, horizontal scaling, and direct SQL queries.
Example
Here's a zero-downtime redeployment scenario, where views continously serve data from the most recent instance to go live.
- App 1234starts up. It creates and begins writing to a table named1234__account.
- App 1234completes the historical backfill. It creates a view namedaccountthat points at1234__account.
- App 5678starts up. It creates and begins writing to a table named5678__account.
- App 5678completes the historical backfill. It updates theaccountview to point at5678__account.
- App 1234shuts down.
- App 5678continues to serve live data via theaccountview.
Crash recovery
If a Ponder app running ponder start crashes and restarts, it will attempt to resume indexing where it left off. Read more about the instance lifecycle and crash recovery mechanism.