Architecture & Security

Cross-Cloud Disaster Recovery: Building a Resilient Oracle DR Solution from AWS to Azure

September 11, 2026
Cross cloud disaster recovery
7 min read
TL;DR
  • Running Oracle on AWS and need a standby in Azure? Targets here were a 15-minute RPO and a 1-hour RTO, with no third-party tooling.
  • Data Guard wasn't available, so the design falls back to archive log shipping with continuous recovery on the standby.
  • Network: site-to-site IPSec tunnel between AWS Virtual Private Gateway and Azure Virtual Network Gateway. Ports 22 and 1521 opened between the two database private IPs
  • Replication: cron runs rsync over SSH every 5 minutes, pushing archive logs to Azure. A loop on the standby applies them while the database stays in MOUNT.
  • Monitoring: apply-lag alert on sequence gap, plus an independent heartbeat alert. Without both, the standby drifts silently.

Running a production database in one cloud is no longer enough for a lot of teams. Regulatory pressure pushes for geographic separation. Architecture reviews flag single-provider dependency. Procurement wants leverage at renewal time. Whatever the driver, the question lands on the database team: can we keep this database alive in a second cloud and if the primary region disappears, can we actually fail over?


It's a fair question, and on paper the answer is well-known: replicate the database. In practice, the path gets narrower fast. Some replication technologies are tightly coupled to managed services that don't exist on the other side of the cloud boundary. Some assume both ends sit inside the same provider's network fabric. Some are gated behind premium tiers your organization hasn't licensed. By the time you've intersected your database, your two clouds, your network topology, and your RPO/RTO targets, the set of supported, blessed solutions can be empty.


That's the situation we recently worked through with a customer running Oracle on AWS, with a standby that needed to land on Azure. The targets a 15-minute RPO and a 1-hour RTO. This post walks through the architecture we landed on, the tradeoffs behind it, and how it's held up in testing.

Why archive log shipping

Cross-cloud replication for Oracle SE2 pushes the design toward an older, more mechanical technique: archive log shipping with continuous recovery on the standby. The approach predates most managed replication services, and it's stayed relevant for exactly the reason it works here it doesn't assume anything about the network fabric, the managed service surface, or the licensing tier underneath.


The approach is straightforward in concept. The primary database generates archive logs as it runs. Those logs are copied to the standby site. A standby database, held in MOUNT state, applies the logs continuously. If the primary fails, the standby is recovered to the last applied log, activated, and opened read-write.


The requirements we were designing against:


  • Primary on AWS, standby on Azure

  • RPO of 15 minutes, RTO of 1 hour

  • No third-party replication tooling

  • Minimal day-2 operational overhead

Architecture Overview

The architecture we implemented uses a proven approach: manual archive log shipping combined with continuous recovery on the standby site. While it may not be as automated as Data Guard, it delivers reliable disaster recovery at a fraction of the cost.

Oracle DR architecture with primary database on AWS shipping archive logs over rsync to an Oracle standby on Azure via site-to-site IPSec VPN

Primary Site (AWS)


  • Oracle database in read-write mode

  • Local archive log destination

  • Automated rsync job via cron

Network Layer


  • Site-to-site IPSec VPN tunnel

  • AWS Virtual Private Gateway ↔ Azure Virtual Network Gateway

  • Secure, encrypted connectivity between clouds

Standby Site (Azure)


  • Oracle SE database provisioned via Tessell

  • Database in MOUNT mode

  • Continuous recovery script applying incoming archive logs

The beauty of this setup? Archive logs generated on the primary database in AWS are automatically pushed to Azure every few minutes via rsync over SSH. On the Azure side, a recovery script continuously applies these logs, keeping the standby database synchronized.

Performance Metrics

For this customer, the DR solution delivers:


  • RPO (Recovery Point Objective): 15 minutes

  • RTO (Recovery Time Objective): 1 hour

These metrics strike a balance between data protection and operational practicality for most business-critical applications. In practice, the rsync interval can be tightened if the workload demands a smaller RPO, at the cost of more frequent log switches on the primary.

Network: the IPSec VPN

AWS and Azure VPN components for a site-to-site IPSec tunnel, showing Virtual Private Gateway, Customer Gateway, Virtual Network Gateway, and Local Network Gateway

Before walking through the build steps, it helps to see the moving parts.


Both sides follow the same pattern: a real gateway that terminates the tunnel, a reference object that represents the peer, and a connection resource that pairs them with the pre-shared key.

RoleAWSAzure
Local tunnel endpointVirtual Private Gateway (VGW)Virtual Network Gateway (VNG)
Reference to the peerCustomer Gateway (CGW)Local Network Gateway (LNG)
Connection resourceSite-to-Site VPN ConnectionVNet Connection

Prerequisites

  • Active AWS and Azure subscriptions

  • Non-overlapping CIDR blocks on the two sides

  • Administrative access to create networking resources in both clouds

StepMicrosoft AzureAmazon Web Services (AWS)
1. Network Foundation1) Create Resource Group. 2) Create a Virtual Network (10.5.*.*/16). 3) Create a Private Subnet (e.g. 10.5.*.*/24) for VMs. 4) Create a Gateway Subnet (e.g. 10.5.*.*/27). Note: this specific name is required for VPN gateways.1) Create VPC Environment (10.10.*.*/16). 2) Configure Public Subnets (for NAT/IGW) and Private Subnets (for workloads). 3) Ensure Internet Gateway and NAT Gateways are configured for basic internet access.
2. Provision GatewaysCreate Virtual Network Gateway: 1) Search for Virtual Network Gateway. 2) VPN Type: Route-based. 3) Associate it with the VNet created above. 4) Create a new Public IP Address.Create Virtual Private Gateway (VGW): 1) Create a VGW resource. 2) Select Actions > Attach to VPC and select your VPC.
3. Establish AWS ConnectionWait for the Virtual Network Gateway public IP to be assigned. Once deployed copy the Public IP from the Overview blade.Create Customer Gateway: 1) Create a Customer Gateway. 2) Input the Azure Public IP. Then create VPN Connection: Site-to-Site VPN Connection with a) Target: Virtual Private Gateway b) Customer Gateway: the one created above c) Static IP Prefix: Azure CIDR (10.5.*.*/16).
4. Exchange KeysWait for configuration data.Download Configuration: 1) Select the VPN Connection. 2) Click Download Configuration with Vendor: Generic / Platform: Generic / Software: Vendor Agnostic. 3) Open the file to find the AWS Tunnel 1 Public IP and the Pre-Shared Key (PSK).
5. Configure Azure Local GatewayCreate Local Network Gateway: 1) Create a Local Network Gateway resource. 2) IP Address: the AWS Tunnel 1 Public IP from the config file. 3) Address Space: the AWS VPC CIDR (10.10.*.*/16).Wait for Azure configuration.
6. Finalize ConnectionCreate Connection: 1) Go to the Virtual Network Gateway > Connections. 2) Click Add. 3) Connection Type: Site-to-Site (IPsec). 4) Select the Local Network Gateway created in Step 5. 5) Paste the Pre-Shared Key (PSK) from the AWS config file.No action required.
7. RoutingAzure typically propagates routes automatically via the Local Network Gateway settings.Update Route Tables: 1) Go to VPC Route Tables (Public and Private). 2) Add a new route with Destination: 10.5.*.*/16 (Azure CIDR) and Target: Virtual Private Gateway.

Security groups and NSGs

The tunnel itself doesn't grant traffic — you still need to allow it. Between the two database private IPs, open:


  • TCP 22 for rsync over SSH

  • TCP 1521 for the Oracle listener (used by applications during and after failover)

Keep the allow lists scoped to the specific private IPs, not the full CIDRs.

Database setup

With the network in place, the database work is the familiar physical standby pattern.


  1. Provision the standby. Stand up an Oracle instance on Azure with the same database name, version, and patch level as the primary. We provisioned through Tessell's control plane, which made matching the AWS configuration a parameter rather than a manual checklist (more on that below).

  2. Establish passwordless SSH between the two database hosts, using key-based authentication for the Oracle OS user. This is what rsync will use.

  3. Configure the primary for archive log mode, with a local archive destination sized for at least a day of log generation plus a buffer.

  4. Prepare the standby as a physical standby restore from a primary backup, mount in standby mode, and confirm it can apply a manually-shipped log before automating.

  5. Deploy the automation: the log-shipping cron on the primary and the recovery loop on the standby.

The two scripts

Archive log shipping (primary, AWS). A cron job runs every 5 minutes. It rsyncs new files from the local archive destination to the standby host over SSH. rsync's delta transfer means re-runs are cheap, and partial transfers are resumed cleanly.


Archive log recovery (standby, Azure). A loop on the standby host watches the incoming archive directory and applies new logs to the mounted standby database. The database stays in recovery mode throughout — it's never opened.


Keep both scripts idempotent and log their actions. The single most useful thing during a real failover is being able to read, at a glance, which log was last shipped and which was last applied.

Monitoring the pipeline

A log-shipping pipeline fails quietly: the cron stops, a disk fills, an SSH key expires — and the standby drifts without anyone noticing. Two checks close that gap:


  • Apply-lag alert. Compare the highest archived sequence on the primary against the last applied sequence on the standby. Alert if the gap exceeds a threshold (we alert past two shipping intervals).

  • Heartbeat alert. Alert if no new log has been applied on the standby within N minutes, independent of the primary-side view this catches the case where the primary-side monitoring is down too.

Both checks are a handful of lines of SQL and shell, and they are the difference between a DR site and a false sense of one.

Failover

A region-level AWS outage triggers a defined sequence. None of the steps are reversible without a rebuild, so the runbook should be followed deliberately, not improvised.

RoleAWSAzure
Local tunnel endpointVirtual Private Gateway (VGW)Virtual Network Gateway (VNG)
Reference to the peerCustomer Gateway (CGW)Local Network Gateway (LNG)
Connection resourceSite-to-Site VPN ConnectionVNet Connection

For most production applications, the full sequence completes well inside the 1-hour RTO. The variable is step 4 how quickly application configuration can be updated and how quickly connection pools drain and reconnect.


A planned failback to AWS is a separate exercise: rebuild the original primary as a fresh standby of the now-promoted Azure database, let it catch up, then run the failover in reverse.

Keeping two clouds identical over time

Most of what's described above is mechanical: configure a tunnel, ship logs, apply logs. The piece that scales poorly by hand is keeping the standby's configuration version, patches, parameters, storage layout identical to the primary's, in a different cloud, over time.


Provisioning the Azure standby through Tessell's control plane collapsed that work into a single parameterized deploy. The standby came up on Azure with the same Oracle version, the same patch level, and a matching storage layout as the AWS primary, because both were described by the same configuration rather than two parallel manual builds.


Beyond the initial provisioning, the same control plane handles automated backups and point-in-time recovery on the standby. That matters more than it sounds: a physical standby protects against site loss, but it doesn't protect against logical corruption a bad DELETE on the primary becomes a bad DELETE on the standby a few minutes later. PITR on the Azure side gives a second, independent recovery path for exactly that case.


The two databases also show up in a single inventory rather than as two independent installations, which makes drift between them visible.

FAQs

Yes. The constraint is usually which method is available to you. Many replication options assume both ends sit inside the same provider's network fabric or depend on managed services that don't exist across the cloud boundary. Archive log shipping with continuous recovery on the standby makes no assumption about either, which is why it works here.

The primary database generates archive logs as it runs. Those logs are copied to the standby site, where a standby database held in MOUNT state applies them continuously. If the primary fails, the standby is recovered to the last applied log, activated, and opened read-write.

This deployment targets a 15-minute RPO and a 1-hour RTO, with logs shipping every 5 minutes. The rsync interval can be tightened if the workload demands a smaller RPO, at the cost of more frequent log switches on the primary.

A site-to-site IPSec VPN tunnel between the AWS Virtual Private Gateway and the Azure Virtual Network Gateway. Both sides follow the same pattern: a gateway that terminates the tunnel, a reference object representing the peer, and a connection resource that pairs them with the pre-shared key. CIDR blocks on the two sides must not overlap.

TCP 22 for rsync over SSH and TCP 1521 for the Oracle listener, which applications use during and after failover. Scope the allow lists to the specific database private IPs rather than the full CIDR ranges.

Two checks. An apply-lag alert comparing the highest archived sequence on the primary against the last applied sequence on the standby. And a heartbeat alert that fires if no new log has been applied within N minutes, which catches the case where primary-side monitoring is down too.

No. A physical standby protects against site loss, not logical corruption. A bad DELETE on the primary becomes a bad DELETE on the standby a few minutes later. Point-in-time recovery on the standby side gives an independent recovery path for that case.

Failback is a separate exercise, not a reversal of the failover steps. Rebuild the original primary as a fresh standby of the now-promoted Azure database, let it catch up, then run the failover in the opposite direction.

Related Blogs