SaaS Cloud Migration to Dedicated Servers: Guide
Migrating your SaaS from the public cloud to dedicated servers means moving workloads from shared or otherwise provider-managed cloud infrastructure to physical servers reserved for your workloads. You usually make the move for one of three reasons: the cloud bill keeps rising, performance varies under load, or you have data-residency, contractual, regulatory, or sovereignty requirements.
The sections ahead follow the move in the following order: assessing whether your workload fits dedicated hardware, sizing the new environment, replicating your application and data, shifting traffic without an outage, and running the platform you now control.
#Why move your SaaS from the cloud to dedicated servers?
Three motivations drive almost every migration: predictable cost at steady load, consistent performance without a hypervisor in the path, and direct control over hardware and data location. Each one grows sharper as your product matures and its traffic settles into a predictable pattern.
#Cost predictability at steady state
Public-cloud pricing is often most advantageous when elasticity is valuable; sustained workloads can make dedicated infrastructure comparatively attractive. You rent capacity by the hour, then pay again for outbound data transfer, provisioned input/output operations per second (IOPS), and the premium attached to every managed service in your stack.
A dedicated server inverts the model. You typically pay for the reserved machine rather than per virtual CPU, with additional charges depending on the provider and configuration. Most plans include a generous monthly traffic allowance rather than metering each gigabyte that leaves the rack.
The most public example of the arithmetic comes from 37signals, the company behind Basecamp and HEY. Its CTO reported cutting the annual cloud bill from a $3.2 million run rate to $1.3 million, saving almost $2 million per year, with projected savings exceeding $10 million over five years.
#Performance without the virtualization layer
On many cloud VM families, a vCPU corresponds to a hardware thread, although the exact CPU model and virtualization behavior depend on the instance type and provider. Amazon's own EC2 documentation states that each vCPU is a thread of a CPU core on most instance families.
Contention on that shared host shows up as jitter, which shows up in your 99th-percentile response times. Dedicated hardware removes the virtualization layer entirely, so every core and every byte of RAM serves your workload alone.
Storage follows the same logic. A network-attached block volume adds a network round-trip to every read that misses cache. A local NVMe drive attaches over the PCIe bus inside the same chassis, with no network hop in the read path.
Also read: Cloud vs Dedicated Server Cost
#Control over data location and hardware
Single-tenant machines give you physical isolation rather than logical isolation. Dedicated hardware can provide additional physical isolation control, but it does not, by itself, establish PCI DSS compliance or satisfy GDPR compliance or data residency requirements.
You remain responsible for implementing and documenting the required security, access control, data protection, and governance measures.
You also choose the components. You can select a specific processor, pin a database to one memory node, enable huge pages, or load a custom kernel module. Most of those options stay out of reach above a hypervisor.
#When the public cloud remains the better fit
Dedicated servers suit predictable workloads, but many SaaS workloads are not. If your product sees tenfold seasonal spikes, still changes shape every sprint, or runs without a dedicated operations staff, elastic infrastructure serves you better.
Global edge delivery is another case where cloud services win outright, and short bursts of GPU training rarely justify owning the hardware. Hybrid setups are common and entirely reasonable. You can keep compute and databases on dedicated servers, while content delivery and burst capacity stay in the cloud.
| Factor | Public cloud instances | Dedicated servers |
|---|---|---|
| Cost model | Hourly instance rate, plus egress, IOPS, and managed service premiums | Fixed hourly or monthly rate with a bandwidth allowance in the plan |
| Compute access | A vCPU typically maps to one hardware thread on a shared host | Every physical core and thread belongs to one tenant |
| Storage path | Network block storage adds a round-trip per uncached read | Local NVMe attaches over the PCIe bus |
| Isolation | Logical, enforced by the hypervisor | Physical, one tenant per machine |
| Capacity changes | New instances in seconds | New machines in minutes to days, depending on build type |
| Operational ownership | The provider patches the platform layer | You patch the platform layer |
Knowing which column your product belongs in requires numbers rather than instinct, which is where the assessment begins.
Rent Dedicated Servers
Deploy custom or pre-built dedicated bare metal. Get full root access, AMD EPYC and Ryzen CPUs, and 24/7 technical support from humans, not bots.
#Assess your SaaS for cloud migration
A migration assessment produces three artifacts: a complete inventory of the services you depend on, a utilization baseline measured from production, and a cost model that includes the transfer itself. Skip any of the three, and the migration turns into guesswork.
#Inventory every service you actually depend on
Start by listing what runs today, then sort each item by how hard it is to reproduce. Portable components move almost unchanged. Replaceable components require an equivalent, whereas provider-specific components depend on a vendor's proprietary API.
Containers, PostgreSQL, MySQL, Redis, Nginx, and anything you already self-manage fall into the portable group. Managed services fall into the replaceable group, and the table below maps the common ones.
| Factor | Public cloud instances | Dedicated servers |
|---|---|---|
| Cost model | Hourly instance rate, plus egress, IOPS, and managed service premiums | Fixed hourly or monthly rate with a bandwidth allowance in the plan |
| Compute access | A vCPU typically maps to one hardware thread on a shared host | Every physical core and thread belongs to one tenant |
| Storage path | Network block storage adds a round-trip per uncached read | Local NVMe attaches over the PCIe bus |
| Isolation | Logical, enforced by the hypervisor | Physical, one tenant per machine |
| Capacity changes | New instances in seconds | New machines in minutes to days, depending on build type |
| Operational ownership | The provider patches the platform layer | You patch the platform layer |
Treat the provider-specific group honestly rather than dismissively. Serverless functions, proprietary NoSQL stores, managed identity providers, and vendor-specific streaming services all carry real engineering value, and rewriting them costs real time.
You have three options for each provider-specific component: rewrite it to use an open equivalent, keep it in the cloud and access it over the public internet, or defer it to a later phase. Choosing deliberately beats discovering the lock-in mid-cutover.
#Baseline what your workload really consumes
Cloud instances hide utilization behind abstraction, so measure directly. Collect at least two weeks of data covering a full business cycle, including your worst day. The sysstat toolkit, which provides the sar and iostat utilities, records exactly these figures on Linux hosts.
From that output, record five numbers per service: 95th percentile CPU, resident memory working set, peak read and write IOPS, sustained network throughput, and monthly outbound transfer volume. Outbound transfer deserves special attention because it scales with product usage rather than with provisioned capacity.
Right-sizing follows directly from those numbers. Consider a platform running twelve 8-vCPU instances that average 22% CPU and peak at 60%. The fleet rents 96 vCPUs, but vCPU counts do not translate directly into equivalent physical-core capacity. Use production utilization data, representative load tests, and normalized CPU performance benchmarks to size dedicated servers for peak demand and failure scenarios.
Two dedicated servers built on 32-core AMD EPYC 9354P processors could serve as a starting point for this workload, but the configuration should be validated through representative load testing. Size the fleet so that the remaining servers can handle peak demand if a machine fails, and verify the result against CPU, memory, storage, and network requirements rather than assuming equivalent performance based on vCPU counts.
#Model the cost and the data transfer
Compare total cost against total cost, not instance price against server price. On the cloud side, add compute, block storage, snapshots, egress, managed service premiums, load balancer hours, and the support plan. On the dedicated side, add server subscriptions, extra bandwidth, and the engineering hours required for the migration.
Transfer time needs its own calculation. Eight terabytes takes about 18 hours to move at the full rate of a 1 Gbps link, and closer to 25 hours at a realistic 70% of that rate.
Exit charges are also shrinking on a legal timetable. From 12 January 2027, the EU Data Act stops providers from charging for switching, including data egress, and during the transition that started on 11 January 2024, they may only pass on the costs they actually incur.
#Decide what moves and in what order
Score each component on three axes: how portable it is, how much damage a failure would cause, and whether it holds state. Stateless services score low on risk and move first. Databases score high on all three and move last, after you have rehearsed the process.
Availability requirements set the floor for how many machines you buy. One server, however well specified, remains one server. For revenue-critical tiers, use N+1 capacity where practical: provision enough aggregate capacity to handle peak demand while retaining one equivalent failure unit, so the tier can continue operating at the required service level after the loss of one machine.
A three-node HA design can preserve a majority/quorum when one node fails if the HA system is configured appropriately. With the target shape decided, the next job is building it.
#Prepare your dedicated server environment
Build the destination as a complete parallel environment before a single byte of production data moves. Running both stacks side by side makes a low-risk cutover possible, at the cost of one or two months of overlapping infrastructure spend.
#Size the fleet by role
Different roles stress different components, so specify each tier against what it actually does. The edge tier terminates TLS, the protocol that encrypts traffic between your users and your servers, and forwards requests to the application nodes behind it.
The following configuration is an illustrative starting point for a mid-sized SaaS platform serving a few thousand tenants and handling roughly 2,000 requests per second at peak; actual hardware requirements should be validated against workload characteristics, resource utilization, performance targets, and failure scenarios.
| Role | Count | Suggested hardware | Reason for the specification |
|---|---|---|---|
| Edge and TLS termination | 2 | 8-core AMD Ryzen 7700X or Intel Gold 5315Y, 32 GB RAM | Connection handling rewards clock speed more than core count |
| Application nodes | 3 | 24-core AMD EPYC 9254P, 128 GB RAM | Cores scale with concurrent request handlers |
| Database nodes | 3 | 32-core AMD EPYC 9354P, 384 GB RAM, 2x NVMe in RAID 1 | Memory holds the hot working set, NVMe absorbs write bursts |
| Object storage | 2 | 24-core CPU, 64 GB RAM, 4x 4 TB NVMe | Capacity and throughput matter more than compute |
| Observability | 1 | 16-core CPU, 64 GB RAM, 2 TB storage | Metrics retention drives disk, not CPU |
Two rules of thumb govern the database tier. Size RAM so the active working set fits in memory, since memory access runs orders of magnitude faster than even NVMe storage. Size storage at roughly three times the current database volume, so maintenance operations, index rebuilds, and a year of growth all fit.
Provisioning speed shapes how you stage the work. You can deploy Cherry Servers' instant dedicated servers in about 12 minutes, while custom builds with specific processors, memory, or GPU accelerators take 1 to 72 hours, averaging around 2 hours.
Order the custom database machines first, then fill in the stateless tiers. The sequencing keeps your schedule tight.
#Lay out storage, memory, and network
Separate the operating system from the data. Two mirrored drives in RAID 1 host the OS, while dedicated NVMe drives store database files.
Place the database write-ahead log on its own device whenever possible. The PostgreSQL documentation describes a separate WAL disk as advantageous because log writes are sequential, while data writes are random.
Insist on ECC (Error-Correcting Code) memory for any node holding state. ECC detects and corrects single-bit memory errors that would otherwise corrupt rows silently over months of uptime.
On the network side, place all internal traffic on a private VLAN subnet. Database and cache traffic should never cross the public internet.
Attach a floating IP address to the edge tier so you can move the public entry point between machines during failover. Confirm that DDoS filtering covers your uplinks.
Out-of-band access matters more than you might expect. An IP KVM console lets you access the BIOS, monitor the boot sequence, or mount a rescue ISO when SSH stops responding.
#Rebuild the platform layer with code, not hands
Hand-built servers drift apart within weeks. Define every node in Terraform or OpenTofu, describe every package and file in Ansible, and let cloud-init handle first boot, so a replacement machine reaches a known state without manual work.
Apply kernel settings in the same automated pass. Setting vm.swappiness to a low value, such as 1, biases the Linux kernel against swapping anonymous application memory, but does not disable swapping. Test the setting under realistic memory pressure before applying it to production database servers.
#Set the security baseline before any data lands
Default-deny firewall rules come first. Permit 80/443 publicly as required; restrict SSH to the management network/VPN, or remove public SSH entirely.
Administrative access should be through a WireGuard tunnel rather than a public SSH port. Certificates come next. TLS grew out of the older SSL protocol, and an ACME client such as certbot can renew your certificates without human intervention.
Secrets need a new home once cloud instance roles disappear. HashiCorp Vault suits you if you want dynamic credentials and audit trails, while SOPS with age suits smaller setups that prefer encrypted files in Git.
Set up backups now rather than after cutover. Migrating onto infrastructure with no restore path is the one failure mode with no recovery.
#Migrate your SaaS application and data
Move stateless components first, replicate stateful data continuously, and keep the cloud environment authoritative until the moment you switch. Sequence the work that way, and every step before cutover stays reversible.
#Port the application layer first
Package each service as a container image and push it to a registry you control, such as a self-hosted Harbor instance. Building images in continuous integration (CI) and pushing them to dedicated nodes changes almost nothing about your daily development workflow.
Orchestration deserves a deliberate choice, and all three options have legitimate places. Docker Compose, or Kamal, keeps operations minimal and fits well if you run fewer than roughly ten services across a handful of nodes.
Nomad sits in the middle, handling scheduling and rolling deployments without the weight of a full cluster platform. Lightweight Kubernetes distributions such as k3s or RKE2 are best suited if you already run managed Kubernetes, since most manifests port over with edits to storage classes and ingress definitions.
Cloud-specific assumptions inside application code cause more delays than any of the above. Audit for instance metadata calls, region environment variables, SDK clients that default to a provider endpoint, and pre-signed URL generation that assumes one object storage hostname.
#Replicate the database with logical replication
The database sets the critical path, so start it early. Logical replication continuously copies rows from the cloud database to the dedicated one, while production continues serving from the cloud, thereby shrinking the eventual write-freeze window to minutes.
Logical replication requires the source's wal_level parameter to be set to logical, which takes effect only on server restart. The replication connection user needs the appropriate replication privileges and pg_hba.conf must permit the connection. Once the source allows it, create a publication covering the tables you intend to move.
CREATE PUBLICATION saas_pub FOR ALL TABLES;
Logical replication does not include schema, so copy the structure separately using pg_dump --schema-only and load it into the target before subscribing. The subscriber then connects, runs an initial table copy, and streams every subsequent change.
CREATE SUBSCRIPTION saas_sub
CONNECTION 'host=source.example.com port=5432 dbname=appdb user=migrator sslmode=require'
PUBLICATION saas_pub;
Watch lag throughout the sync by querying the pg_stat_subscription view on the target. The initial copy of a large table can take hours, and you need to know exactly when it finishes.
Three gaps in logical replication cause most cutover failures, and the official restrictions list documents them all. Sequences do not replicate, so every serial column restarts from its old position unless you reset it.
Schema changes do not replicate either, which is why deployments freeze during the migration window. Large objects are entirely left behind and require a separate copy.
Reset each sequence with one statement per table, and put those statements in your cutover script.
SELECT setval('users_id_seq', COALESCE((SELECT max(id) FROM users), 1), (SELECT count(*) > 0 FROM users));
For a GTID-enabled MySQL deployment, take a consistent dump with mysqldump --single-transaction --set-gtid-purged=ON, load it on the target, and configure the target as a replica using GTID-based automatic positioning.
Before starting replication, verify that binary logging, GTID mode, the replication user, and the required source and replica settings are enabled on both servers. Then configure the source connection with SOURCE_AUTO_POSITION=1 and start replication.
The exact settings depend on the MySQL version and topology, so validate them against the MySQL replication documentation before migration.
#Move object storage and rebuild the rest
Object data usually represents the largest volume and the simplest migration. An S3-compatible target such as MinIO accepts the same API calls your application already makes, so the change amounts to a new endpoint and new credentials.
Copy the buckets with rclone, raising its parallel transfer and checker counts so a multi-terabyte sync saturates the link. Run the sync once during preparation and again right before cutover. The second pass completes in minutes because it transfers only objects that have changed.
Caches, queues, and search indexes rarely justify a migration at all. Rebuild Redis from source data, drain queues instead of copying messages, and reindex search from the primary database once replication has caught up. Each path runs faster than a copy, leaving you with a clean state.
#Keep a path back
Before cutover, keep the cloud environment available and maintain current backups with point-in-time recovery (PITR). Define the rollback procedure before switching writes to the dedicated environment. If rollback is required after new writes begin, do not assume that reverse replication provides a safe rollback path.
Instead, use a controlled reverse migration or reconcile the writes made on the dedicated environment before restoring the cloud environment as the primary. The rollback plan should be rehearsed and should explicitly account for data written after cutover.
#Test and cut over to dedicated servers
Validate correctness and capacity on the new hardware, then move traffic in stages while the old environment stays warm. A migration rehearsed twice in staging usually requires a write-freeze window of 5 to 20 minutes.
#Validate correctness and capacity
Run your full functional test suite against the dedicated environment using replicated production data. Integration tests catch the cloud-specific assumptions that code review missed, particularly around object storage URLs and identity tokens.
Load testing comes next, and it needs a realistic traffic mix rather than a synthetic hammer. A tool such as k6 can model your real endpoint distribution. Compare 95th percentile latency, 99th percentile latency, error rate, and CPU saturation at peak against your cloud baseline.
Data integrity checks belong in the same phase. Compare row counts per table, then hash each row individually and fold the results into a running total across primary key ranges, since hashing an entire table into a single value produces enormous intermediate results and can exhaust memory on large tables.
Matching counts with mismatched hashes usually indicates differences in encoding, time zone, or numeric formatting. Each is far easier to fix before cutover than after.
#Shadow real traffic before you commit
Mirroring sends a copy of each live request to the new stack while your users continue to receive responses from the old one. Nginx supports the technique directly through its mirror module, which forwards a duplicate subrequest to an internal location and discards the mirrored response.
Discarded responses make shadowing safe for read traffic and dangerous for writes, unless the mirrored stack points at a scratch database. Run read traffic through the mirror for 48 hours to surface slow queries and missing indexes under genuine production load.
#Run the cutover
Write the runbook as a timed sequence and assign an owner to each step. The version below is suitable for a database-backed SaaS platform with an object storage tier.
-
T-72 hours: lower the DNS TTL (Time To Live) on your production records from 3600 seconds to 60 seconds, so cached lookups expire quickly.
-
T-24 hours: freeze schema changes and application deployments, since logical replication does not carry DDL (Data Definition Language), and a mid-migration schema change breaks the subscriber.
-
T-1 hour: run the final object storage sync and confirm replication lag sits near zero.
-
T-0: switch the application into read-only mode, then emit a logical message on the publisher with
pg_logical_emit_messageand note the log sequence number (LSN) it returns. Pollpg_stat_replicationon the publisher untilreplay_lsnfor your subscription passes that marker, andpg_wal_lsn_diffagainst it reads zero across two consecutive checks. -
T+2 minutes: confirm the subscriber has replayed past the marker LSN, reset every sequence on the target, then stop replay with
ALTER SUBSCRIPTION saas_sub DISABLErather than dropping it. Make the dedicated database the write target, and remove the subscription and its replication slot only after the new environment clears your observation window. -
T+5 minutes: repoint DNS records or flip the edge proxy upstream to the new backend pool.
-
T+8 minutes: lift read-only mode and run smoke tests against authentication, billing, and your two highest-traffic endpoints.
-
T+15 minutes onward: watch error rate, queue depth, replication lag, and 99th percentile latency continuously for the first hour.
Keep the cloud environment running for seven to fourteen days after the switch. The cost of two overlapping environments for two weeks is trivial next to the cost of an unrecoverable cutover.
#Test the rollback you hope never to use
Define abort criteria in writing before the cutover starts. An error rate above 2% for five minutes, or 99th percentile latency at double the baseline for ten minutes, are examples of thresholds a stressed engineer can act on at 3 a.m.
Rehearse the rollback in staging alongside the migration itself. A rollback procedure that nobody has executed is a document, not a safety net.
#Monitor and optimize after migration
Owning the hardware means owning the observability that came bundled with your cloud provider. The upside is that you can now tune layers that the hypervisor previously hid from you.
#Build the observability you no longer rent
Prometheus with node_exporter covers system metrics, Grafana handles visualization, Alertmanager routes notifications, and Loki collects logs. Purpose-built exporters for PostgreSQL, Redis, and Nginx fill in the application layer.
Hardware telemetry is a genuinely new responsibility. Four signals deserve alerts of their own:
-
Drive health and wear. The smartmontools utility
smartctlreports reallocated sectors on SATA drives, andnvme smart-logreports the percentage-used counter on NVMe devices. Rising values on either counter signal a drive nearing replacement. -
Correctable memory errors. The rasdaemon service records ECC corrections. A rising correction rate warns you about a failing memory module days before it fails outright.
-
RAID array state. A degraded mirror that nobody notices becomes a data loss event when the second drive fails. Alert on array state changes the moment they happen.
-
Thermals and power. IPMI (Intelligent Platform Management Interface) sensors expose inlet temperature and fan status. Both correlate with thermal throttling under sustained load.
#Tune what the cloud never let you touch
Check the CPU frequency scaling governor first. The kernel's cpufreq documentation describes the performance governor as maintaining the processor at its highest available frequency, which better suits latency-sensitive request handling than power-saving defaults. Set it with the cpupower frequency-set -g performance command.
On dual-socket machines, memory access times differ depending on which socket owns the memory bank. The kernel documents the effect as NUMA (Non-Uniform Memory Access). Measure NUMA effects first. Use CPU/memory affinity selectively when benchmarking demonstrates a benefit.
PostgreSQL configuration deserves a pass now that the hardware is known rather than assumed. The official documentation recommends starting shared_buffers at 25% of memory on a dedicated database host and lowering random_page_cost to around 1.1 on solid-state storage, so the planner stops overpricing random reads.
Set effective_cache_size to your estimate of the memory available for disk caching, and mount data filesystems with noatime to remove an access-time write on every read.
#Re-baseline capacity and cost
Compare the numbers you captured during assessment against production reality after two weeks of steady traffic. Cost per thousand requests and 99th-percentile latency are the two metrics that indicate whether the migration achieved its goals.
Capacity planning changes shape on dedicated hardware. Adding a node takes minutes for an instant server and up to a few days for a custom build, so keep 25-30% headroom rather than the thin margins autoscaling allows.
Treat sustained resource saturation, SLO degradation, or insufficient failure headroom as scaling signals; set thresholds from your workload.
#Protect the data you now own
Follow the 3-2-1 rule: three copies, two media types, one off-site. pgBackRest handles PostgreSQL backups with write-ahead log archiving and retention policies, which together give you point-in-time recovery rather than nightly snapshots.
Cherry Servers includes 100 GB of network-attached backup storage with each dedicated server, expandable to 2 TB as your data grows. Keeping backup storage separate from primary disks protects you against both hardware failure and ransomware.
Schedule restore drills quarterly and time them. Knowing that a full restore takes 40 minutes turns an outage into a decision, while not knowing turns it into a crisis.
Optimize your cloud costs with Cherry Servers’ private cloud—a flexible alternative to on-premises infrastructure.
#Conclusion
Moving a SaaS platform from public cloud to dedicated servers is a project with a predictable shape. The assessment turns utilization data and total cost into a defensible decision. Preparation builds a parallel environment, replication keeps both sides in sync until the last moment, and a rehearsed cutover holds the write freeze to minutes.
The observability and tuning work you take on afterward is the price of the control and cost predictability you gain.
Your next step is measurement, not migration. Pull a month of utilization and egress data, map each managed service to a self-hosted counterpart, and price the hardware against dedicated server plans. If your workload needs exact components, a custom dedicated server build lets you specify them.
FAQs
How long does a SaaS cloud migration to dedicated servers take?
A mid-sized migration may take several weeks, but timelines vary substantially. Database replication and application refactoring consume most of that time, while hardware provisioning may be relatively short for prebuilt servers but can take longer for custom hardware or nonstandard requirements.
Will my users experience downtime during the cutover?
A rehearsed cutover using logical replication typically needs a five to twenty-minute read-only window rather than full downtime. If you need zero interruption, you can run dual writes for a period, though the added complexity is significant.
Are dedicated servers cheaper than public cloud for SaaS workloads?
They usually cost less for steady, predictable workloads with high sustained utilization, especially when outbound data transfer is heavy. Spiky or seasonal workloads often remain cheaper on elastic cloud infrastructure.
How do I handle traffic spikes without cloud autoscaling?
Provision 25-30% headroom above peak and keep hourly-billed capacity available for genuine surges. You can also keep a small cloud footprint for overflow, which combines fixed-cost baseline capacity with elastic burst.
What happens to compliance after migrating off managed services?
Single-tenant hardware simplifies physical isolation arguments for PCI DSS and GDPR, but you inherit responsibility for patching, access control, and audit logging. Document the new control ownership before your next audit cycle.
Can I keep part of my stack in the cloud?
Yes, and most production setups do. Content delivery networks, backup replicas, and burst compute resources commonly reside in the cloud, while databases and steady application traffic run on dedicated hardware.
Can I run my SaaS platform on Cherry Servers' dedicated servers?
Yes. You can deploy instant dedicated servers in about 12 minutes with root access, private VLAN networking, and API-driven provisioning, while custom builds support specific CPU, memory, storage, and GPU configurations for demanding workloads.
Get 100% dedicated resources for high-performance workloads.