PostgreSQL PITR restore uses an unexpected recovery target timestamp
misscapuchino
HOBBYOP

4 hours ago

Hi Railway Team,

We're preparing to migrate our production application from SQLite to PostgreSQL on the Railway Hobby plan, and we've encountered a repeatable issue while qualifying PostgreSQL Point-in-Time Recovery (PITR).

The problem

We requested a PITR restore using Railway CLI v5.71.0 with an explicit UTC timestamp:

  • Requested target: 2026-11-04T05:31:00Z
  • CLI confirmation: correctly displayed the requested timestamp.
  • Actual POSTGRES_RECOVERY_TARGET_TIME on the newly created restore service: 2026-11-03 05:36:23.042449+00

The restored service therefore received a recovery target almost 24 hours earlier than requested.

We previously observed similar behavior in the Railway dashboard, where a near-current restore selection changed to the older timestamp during confirmation.

The source database had not received writes since approximately November 3, which might be relevant.

Our questions

  1. Is Railway intentionally replacing the requested PITR timestamp with the source database's last-write timestamp? If so, is this expected behavior?

  2. Does the older POSTGRES_RECOVERY_TARGET_TIME represent the actual recovery target, or could the database still recover to the originally requested timestamp?

  3. What is the recommended way to restore and verify a near-current recovery point when the source database has been idle?

  4. Railway also estimates approximately 22.1 GB of WAL replay for a database of around 42 MB. Does that require 22.1 GB of simultaneous disk capacity, or can WAL be replayed incrementally within a 5 GB Hobby volume?

  5. Is PostgreSQL PITR officially supported for production recovery on the Railway Hobby plan? Our Hobby workspace allows PITR restores, but we would like to confirm that a reliable recovery process is supported without upgrading to Pro.

Our constraints

We need to remain on Railway Hobby with a maximum 5 GB database volume.

We have already performed two isolated restore attempts. Both temporary services and volumes were safely removed. Our original PostgreSQL service and production application were not modified.

We have paused further restore attempts until we understand the timestamp behavior and can confirm a reliable recovery procedure.

Could you please advise whether this is expected PITR behavior, a configuration issue, or a potential platform bug, and recommend a supported solution?

Thank you for your help!

Awaiting Railway Response

2 Replies

Railway
BOT

4 hours ago

We haven't confirmed why the restored service shows POSTGRES_RECOVERY_TARGET_TIME set to 2026-11-03 05:36 UTC instead of the 2026-11-04 05:31 UTC you asked for. So we can't yet say whether the value was deliberately moved to the newest recoverable point or should have kept your timestamp. Treat the value on the restored service as the target recovery actually used, not your original request.

A PITR restore takes the latest base backup at or before the target and replays archived WAL up to the target. If nothing was written to the source between those two times, both targets produce the same data.

To check a near-current recovery point, run railway postgres pitr status on the source to see its restore coverage. After restoring, compare the newest rows on the restored service with the source.

The restored service gets a new volume sized to the larger of the source's volume and your plan's default. We haven't confirmed whether the 22.1 GB replay estimate has to fit on that volume all at once or is pulled from the archive bit by bit, so we can't promise it fits inside 5 GB.

PITR is available for single-node Postgres, Hobby workspaces can run PITR restores, and each restore creates a separate service while leaving the source untouched. The full flow is described on our Point-in-Time Recovery page.


Status changed to Awaiting User Response Railway • about 4 hours ago


Railway

We haven't confirmed why the restored service shows `POSTGRES_RECOVERY_TARGET_TIME` set to 2026-11-03 05:36 UTC instead of the 2026-11-04 05:31 UTC you asked for. So we can't yet say whether the value was deliberately moved to the newest recoverable point or should have kept your timestamp. Treat the value on the restored service as the target recovery actually used, not your original request. A PITR restore takes the latest base backup at or before the target and replays archived WAL up to the target. If nothing was written to the source between those two times, both targets produce the same data. To check a near-current recovery point, run `railway postgres pitr status` on the source to see its restore coverage. After restoring, compare the newest rows on the restored service with the source. The restored service gets a new volume sized to the larger of the source's volume and your plan's default. We haven't confirmed whether the 22.1 GB replay estimate has to fit on that volume all at once or is pulled from the archive bit by bit, so we can't promise it fits inside 5 GB. PITR is available for single-node Postgres, Hobby workspaces can run PITR restores, and each restore creates a separate service while leaving the source untouched. The full flow is described on our [Point-in-Time Recovery page](https://docs.railway.com/volumes/point-in-time-recovery).

misscapuchino
HOBBYOP

4 hours ago

Thank you for the explanation.

We still need assistance from a Railway engineer because two isolated PITR restores have shown the same unexpected recovery target behavior.

The source PostgreSQL database has been idle since November 3. We understand that restoring to November 3 or November 4 might produce identical data if no writes occurred between those dates.

However, we need clarification on two critical points before activating PostgreSQL for our production application:

  1. Recovery timestamp: Does Railway intentionally normalize POSTGRES_RECOVERY_TARGET_TIME to the source database's last-write timestamp, even when the CLI explicitly requests a newer UTC timestamp? Is this expected behavior or a bug? How should we verify PITR correctness for an idle database?

  2. Hobby recovery capacity: Does the estimated 22.1 GB WAL replay require equivalent disk space, or can Railway process it incrementally within a 5 GB Hobby volume? Is there a supported way to confirm recovery feasibility?

Both test siblings were safely deleted, and the original PostgreSQL service remains unchanged.

We cannot proceed with our production database cutover until we have a reliable recovery procedure.

Could a Railway engineer please review these two questions and recommend the supported approach?

Thank you.


Status changed to Awaiting Railway Response Railway • about 4 hours ago


Welcome!

Sign in to your Railway account to join the conversation.

Loading...