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
/ready
and the Healthcheck Timeout to86400
seconds (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_URL
and 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 HTTP200
response immediately after the process starts./ready
: Returns an HTTP200
response once indexing progress has reached realtime across all chains. During the historical backfill, the endpoint returns an HTTP503
response.
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/core";
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
1234
starts up. It creates and begins writing to a table named1234__account
. - App
1234
completes the historical backfill. It creates a view namedaccount
that points at1234__account
. - App
5678
starts up. It creates and begins writing to a table named5678__account
. - App
5678
completes the historical backfill. It updates theaccount
view to point at5678__account
. - App
1234
shuts down. - App
5678
continues to serve live data via theaccount
view.
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.