How I Restore an Entire Server From Backup (And How Long It Takes)
Tutorial

How I Restore an Entire Server From Backup (And How Long It Takes)

The order matters more than people expect, and the answer to "how long will this take" isn't a number I can hand you — it's dominated by your database size, your download bandwidth, and one DNS setting most people only think to change after they needed to.

Published September 23, 202611 min readUpdated Sep 6, 2026

Written by · Full-Stack Agentic AI Software Engineer — AI Agents, Automation & Revenue Systems for GTM/RevOps teams

In brief

What's the actual order of steps to restore a whole self-hosted server from backup, and how long does a full restore realistically take?

The order is provision, harden, restore Docker state, restore volumes, restore the database, re-point DNS, then verify — and each step exists where it does for a reason: you don't restore data onto a server you haven't secured, and you don't send traffic to a server you haven't verified. There's no honest single number for how long the whole thing takes, because it's dominated by three variables that differ for every setup: how large your database is and how long a restore of that size actually takes to run, how much bandwidth you have to pull backups down from offsite storage, and your DNS TTL, which controls how long the world keeps sending traffic to the old, dead server after you've pointed the domain elsewhere. The only honest answer is to time your own drill on your own data, and the single highest-leverage thing to do in advance, not during an incident, is lowering your DNS TTL before you need it — raising it back up afterward if you want, but having it low going in.

  • The restore order is deliberate: provision, harden, Docker state, volumes, database, DNS, verify — restoring data before hardening or verifying before DNS has propagated both create avoidable risk
  • Database restore time scales with database size and the restore method (a pg_dump replay is generally slower than a pg_basebackup restore for large databases), and there's no universal number — time it on your own data
  • Download bandwidth pulling backups from offsite storage is the other major variable — with Cloudflare R2 as the destination, the pull itself is free of egress charges, but it still takes as long as your connection allows
  • DNS TTL controls how long stale traffic keeps hitting the dead server after you've re-pointed the domain — a TTL set low before the incident, not during it, is the single detail most restore plans miss
  • Verification isn't optional or automatic — confirm the app actually serves correct data before considering the restore done, not just that services started without an error

Evidence notes

PostgreSQL backup and restore methods

pg_dump restores by replaying SQL against a fresh database; pg_basebackup restores by placing a physical copy of the data directory, generally faster for large databases; neither is interchangeable with a raw filesystem copy of a running instance.

Cloudflare R2 pricing

Zero egress fees at any usage level, checked September 2026 — pulling a backup down during a restore costs nothing beyond storage already paid for, though the transfer still takes real time over your own connection.

People ask this two ways: "what's the order of operations" and "how long will it take," and only the first one has a clean answer. The order is the same regardless of what's running on your stack. The timing genuinely isn't — it depends on your database size, your bandwidth, and a DNS setting almost nobody remembers to check until the day they need it, so I'm not going to hand you a duration I didn't measure and dress it up as universal. Time your own drill. This is the order to run it in.

The reason this order matters as much as it does is that each step assumes the previous one is actually finished, not merely started. Restore volumes before the platform layer exists to receive them and you're guessing at paths. Point DNS before the app is verified and you've just moved traffic onto something you haven't confirmed works. None of these steps are individually hard — the difficulty in a real restore is almost always sequencing and patience, not any single command. The marketing-operations counterpart to disaster recovery is documented well by XenGrowth's revenue operations work.

Step one: provision a fresh server

Resist the urge to skip ahead to whichever step feels most urgent given whatever actually happened — a full restore drill is valuable precisely because it exercises every step in order, including the ones that feel unnecessary when you're confident you know what broke.

Start from nothing, not from a snapshot of the old box. A fresh VPS provisioned from the same provider — or a different one, if the original scenario is why you're doing this at all — is the correct starting point specifically because it guarantees you're not carrying forward anything wrong with the previous instance, whether that's a subtle corruption, a leftover compromise, or just configuration drift nobody remembers introducing.

Step two: harden it before it touches any real data

This step exists specifically to be done before restoring anything, not after. SSH configuration, firewall rules, whatever baseline security posture your original server had — all of that needs to exist on the new box before real data lands on it, because a freshly provisioned VPS sitting exposed on default settings, even briefly, is exactly the kind of window that turns a routine restore into a second incident layered on top of the first one. Skipping this step to 'get back online faster' is the shortcut that costs the most when it goes wrong. On the operations side of this specifically, The XenGrowth resource library is worth reading.

Step three: restore Docker and platform state

Install Docker, install whatever platform layer you run — Coolify or equivalent — and restore its own configuration backup before touching application data. This gets your orchestration layer back to a known state: projects defined, resource configurations in place, the scaffolding your actual services will run inside of. Nothing application-specific needs to exist yet; this step is purely about having somewhere correct to restore the rest of it into. Reinstalling the platform itself is usually one of the fastest steps in the whole sequence, since it's a standard installer running against a fresh OS rather than a data-dependent operation, which is exactly why it's worth doing carefully instead of rushing past it toward the steps that feel more consequential. Confirm this step actually landed before moving on — open the platform's dashboard and check that your real projects show up, not a blank install. If the configuration restore silently failed, better to find out here than three steps later when the volume restore has nowhere correct to go.

Step four: restore Docker volumes and bind mounts

Pull down whatever's backing your uploaded files, generated assets, and any other persistent volume data, and restore it into the paths your platform configuration expects. Do this before the database restore, not after, so that by the time the app comes up expecting both its files and its data to exist, only one of those two things is still missing rather than both. This is also where backup destination bandwidth first shows up as a real number rather than an abstraction — a volume backup holding years of user uploads can be the single largest transfer in the whole restore, larger than the database dump in plenty of setups, and it's worth checking its size against your connection before you're mid-incident wondering why the download bar hasn't moved.

Step five: restore the database

This is usually the slowest step and the one people most want a duration for. Restore method matters here: replaying a pg_dump means creating a fresh database and running through every statement in the dump in order, which scales with both data volume and schema complexity. Restoring from a pg_basebackup, if that's the method your backup strategy used, means placing a physical copy of the data directory and letting Postgres start from it directly, generally faster for large databases because it skips re-executing every insert. Either way, don't estimate this from a small test database and assume production scales linearly — run the actual restore on your actual backup size if you want a number you can trust. Index rebuilding is the specific part that surprises people: a pg_dump restore recreates indexes after loading the data rather than alongside it, so a table with a dozen indexes can spend meaningfully more time on that step than on loading the raw rows — one more reason a small test dataset understates the real number. XenGrowth on governed AI marketing workflows goes further into AI agents and marketing automation.

Restore method

What it does

Where it scales well vs poorly

pg_dump restore (psql/pg_restore)

Recreates the database by replaying SQL statements from the dump

Simple and portable; slows down more noticeably as data volume and index count grow

pg_basebackup restore

Places a physical copy of the data directory and starts Postgres directly from it

Generally faster for large databases since it skips re-executing every write

WAL replay on top of a base backup

Applies archived transaction logs forward from a base backup to a specific point in time

Adds time proportional to how much WAL has to be replayed since the base backup, on top of the base restore itself

  • Restoring onto a server that hasn't been hardened yet, because rebuilding "felt urgent" — this is how one incident becomes two

  • Restoring the database before confirming which backup is actually the right one, especially after an accidental-deletion scenario where the timing of the restore point matters

  • Assuming a restore that completed without an error also completed correctly, and skipping the verification step because everything looks fine from the platform dashboard

  • Changing the DNS TTL for the first time during the incident itself, which does nothing for traffic that already cached the old, longer TTL

  • Never having run any of this before the day it's needed for real, so every step is being figured out live instead of executed from memory

Step six: re-point DNS — and why the TTL matters more than the record change itself

Updating the A record to point at the new server's IP is the easy part; it's a single change. What actually controls how fast the internet notices is your DNS TTL — the time-to-live value telling every resolver that's already cached your old record how long it's allowed to keep serving that stale answer before checking again. A TTL of 24 hours, which is a common default and one nobody thinks to question until this exact moment, means some fraction of visitors keep getting routed to a dead server for up to a full day after you've already fixed the DNS record. If Cloudflare's proxy sits in front of your domain, changing the record inside Cloudflare itself can take effect faster for traffic that resolves through Cloudflare's own network, but any resolver that already cached the old answer directly, or any client with its own local DNS cache, is still bound by whatever TTL was set when it last looked the record up.

The DNS change takes seconds. Waiting for a high TTL to expire everywhere it's cached can take longer than every other step in this whole restore combined — and it's the one variable you can fix in advance, for free, before you ever need to.

This is the tip people miss, stated plainly: lower your TTL well before any incident, not during one. A TTL of a few minutes, set as a standing default on the records that matter, costs you nothing day to day and means that when you actually need to re-point DNS during a real restore, the world catches up in minutes instead of hours. Raise it back up afterward if you want the marginal DNS-lookup savings of a longer TTL during normal operation — but go into any potential incident with it already low, because setting it low for the first time in the middle of an outage doesn't help you; the stale, high-TTL answer is already cached everywhere that matters by the time you'd make that change.

Step seven: verify — actually verify, not just "it started"

A service starting without throwing an error is not the same claim as the service being correct. Load the app, exercise the path that touches the database, and confirm specific data you know should be there actually is — a known record, a row count, a file that should exist and does. This is the same discipline as verifying any other backup: the absence of an error tells you the mechanics ran, not that the result is right. Check the boring things too, since they're the ones that quietly go wrong: TLS is actually issued and valid for the new server, not just that the app responds on plain HTTP; environment variables came through with the values you expect, not empty strings that happen not to crash anything; and background jobs or scheduled tasks are actually running again, not silently absent because nothing about the platform restore re-registered them. XenGrowth on building one SEO and GEO content system goes further into AI search, GEO and discovery.

So how long does this actually take?

I'm not going to give you a number, because any number I gave you would be a guess dressed up as a fact, and that's exactly the kind of claim this whole cluster is built to avoid. What I can tell you is what the number is made of, so you can go measure your own: database restore time (proportional to data size and restore method), download time pulling your backups from offsite storage (proportional to their size and your bandwidth — free of egress cost on a destination like Cloudflare R2, but not free of time), and DNS propagation time (entirely a function of the TTL you had set before the incident started, not the one you change during it).

What dominates restore time

What actually controls it

What you can do about it in advance

Database restore

Data size, schema complexity, restore method chosen

Test your actual restore on your actual data size at least once, don't extrapolate from a toy dataset

Backup download

Backup size and your connection's download bandwidth

Egress cost isn't the constraint on R2, but transfer time still is — know your realistic throughput

DNS propagation

The TTL that was set before the incident, not the one you set during it

Lower your TTL as a standing default, well before you ever need a fast cutover

Run this whole sequence once, deliberately, on a disposable VPS, before you ever need it for real — time it, write the number down, and repeat the drill periodically as your data grows, because the honest number today is not the honest number in a year. That rehearsed number is worth infinitely more than any duration a blog post could hand you, because it's the one that's actually true for your database, your bandwidth, and your DNS setup, not someone else's.

Further reading from XenGrowth

Where this work meets go-to-market

Working on disaster recovery inside a commercial team? XenGrowth's marketing operations practice publishes operator guides on the revenue side of this work.

Would your restore actually work?

Four questions on the difference between having backups and being able to come back. Most of the failures here happen at restore time, which is the worst possible moment to find them.

1 / 4
Your server is gone. What does infrastructure-as-code get you back?

Apply this article

How to turn insights into execution

A practical sequence for teams turning concepts into production outcomes.

disaster recoverybackupPostgreSQLCoolifyDNSself-hostingtutorials

Audit your current state

Map the bottlenecks and constraints connected to the article’s core problem.

Choose one bounded change

Test the most useful recommendation on one workflow before widening the scope.

Measure what changed

Keep the parts that improve the work, document what failed, and make the next decision from evidence.

Next step

Need help applying this in your stack?

I can translate these patterns into a concrete implementation plan for your team.

Discuss implementationBack to blog

Replies usually within 24 hours.

Next Steps

Continue reading

How to Back Up and Restore a Coolify Server Properly

Coolify's built-in backup covers less than most people assume, and a filesystem copy of a live Postgres data directory is not a valid database backup no matter how confident it looks in a file listing. Here's what actually has to be backed up, and a restore you've actually tried before you need it.

Navigate

How I Back Up PostgreSQL to S3-Compatible Storage on a Schedule

pg_dump, pg_basebackup and WAL archiving aren't three ways to do the same thing — they answer three different questions about how much data you're willing to lose. Here's which one to run, from a container that has no business having shell access to your host, pushed to storage that makes restoring free instead of expensive.

Navigate

The Backup Strategy Every Self-Hosted SaaS Needs (3-2-1, Applied)

3-2-1 is easy to nod along to and easy to get wrong in the specific way that only shows up on the day you need it. Here's what it actually means for one VPS running Postgres and Docker volumes, not the generic version you've already skimmed past twice.

Navigate

How to Self-Host Next.js With Coolify: A Complete 2026 Walkthrough

Every Coolify tutorial stops at "push to deploy." This one covers the parts that actually break a real app: standalone output, a build that OOMs on a small box, env vars baked in at the wrong time, and a health check that would have caught all of it.

Navigate

How to Host Unlimited Domains on One VPS With Free SSL

"Unlimited domains, one server" sounds like a sales pitch, but it's a genuinely accurate description of what SNI-based virtual hosting and Let's Encrypt automation do together. The real ceiling isn't domain count. It's RAM.

Navigate

The Docker Concepts You Need Before Coolify Hides Them From You

Coolify's whole pitch is that you shouldn't have to think about Docker, and most days that's true. The days it isn't are the days a container is 'running' but unhealthy, a redeploy quietly ate a volume, or an env var vanished — and the dashboard doesn't explain any of it.

Navigate

Wildcard Domains With Coolify and Cloudflare

A wildcard certificate can't be proven the way a normal one is — there's no single page to fetch for a domain that doesn't exist yet. That's why it needs a DNS record instead of an HTTP request, and why Cloudflare's API has to be involved at all.

Navigate

Where console.log Actually Goes When You Self-Host Next.js

On Vercel, a console.log just shows up in a dashboard somewhere. Self-hosted, it goes through a chain most people never trace end to end — and the two most common questions I get are why a log doesn't show up at all, and why the disk filled with logs nobody remembers writing.

Navigate

My Complete Self-Hosted Stack for SaaS in 2026

Contabo, Coolify, Docker, Cloudflare, Postgres, R2, Resend, Uptime Kuma, Turborepo and Docker Hub. Here's every piece of the stack I actually run, what each one replaced, and why I picked it over the alternatives.

Navigate

The Simplest Production Architecture for a Bootstrapped SaaS

One box, a handful of managed pieces around the edges, and a very short list of things you're not allowed to build yet. Here's the architecture, priced out to $20 a month, and the exact signal that tells you when to add each thing you skipped.

Navigate