2026-04-17·18 min read·

DORA Art.12: ICT Backup Policies, Restoration, and Recovery for Financial Services — 3-2-1-1-0 Architecture, Location Segregation, and DORABackupChecker (2026)

Post #405 in the sota.io EU Cyber Compliance Series

DORA Chapter II defines a six-domain ICT risk management framework. Article 12 sits immediately after the response and recovery obligations of Art.11 — and where Art.11 governs your business continuity policy and recovery objectives, Art.12 governs the operational backbone that makes those objectives achievable: your backup copies, restoration procedures, and the physical and logical separation that protects those copies from the same incident that triggered the recovery.

The distinction matters for NCA auditors. A financial entity that has a well-documented BCP (Art.11 compliant) but stores backups in the same cloud account as production data — or restores from systems that share a network segment with the compromised source — fails Art.12 regardless of its continuity planning.

This guide covers all four substantive Art.12 obligations, explains the infrastructure architecture that satisfies them, provides a Python self-assessment tool (DORABackupChecker), maps to NIS2 Art.21(2)(c) for dual-regulated entities, and closes with a 25-item NCA audit checklist.


1. Who Is Subject to DORA Art.12

DORA Art.2(1) scope covers the same approximately 22,000 EU financial entities as the rest of Chapter II:

CategoryExamples
Credit institutionsBanks, savings banks, credit unions
Investment firmsBrokers, asset managers, trading firms
Insurance/reinsuranceAll Art.2(1)(c) undertakings
Payment institutionsPSPs, e-money issuers
Crypto-asset service providersCASPs under MiCA
Central counterpartiesCCPs, CSDs
ICT third-party service providersCloud, SaaS in scope via Art.30

Micro-enterprises (fewer than 10 employees, annual turnover ≤ €2 million) receive a simplified path under Art.12(5): they may use basic backup solutions provided by third parties rather than implementing the full Architecture described in Art.12(1)–(4). All other entities must comply with the full requirements.


2. The Four Art.12 Obligations at a Glance

Art.12 imposes four distinct requirements. Each has a separate audit evidence trail:

ParagraphObligationEvidence Required
Art.12(1)Documented backup policy (scope + frequency)Policy document, data classification register
Art.12(2)Backup systems activated without security degradation; periodic testingActivation procedure, test reports
Art.12(3)Physically and logically segregated restore systemsInfrastructure diagram, network segmentation docs
Art.12(4)Backup copies at geographically separate locationLocation evidence (AZ mapping, contract, or cloud region docs)

3. Art.12(1): Backup Policy — Scope and Frequency

Art.12(1) requires financial entities to develop and document:

3.1 Data Scope Classification

The backup policy must explicitly enumerate what is backed up. A common mistake is to back up application data but omit configuration state, secrets vaults, or certificate stores. DORA requires a data criticality register that maps each data category to a backup tier:

DATA CRITICALITY CLASSIFICATION (Art.12(1)(a) Evidence)

Tier 1 — Critical (RTO ≤ 2h, RPO ≤ 15 min)
  ├── Core transaction data (payments, trades, positions)
  ├── Customer account data (KYC, IBAN, authentication state)
  ├── ICT asset configuration (network devices, firewalls, load balancers)
  └── Cryptographic key material (HSM backups, TLS certs, signing keys)

Tier 2 — Important (RTO ≤ 8h, RPO ≤ 1h)
  ├── Application database replicas
  ├── Identity provider (IdP) state
  ├── Audit logs and SIEM data (integrity-preserved)
  └── API gateway configuration

Tier 3 — Standard (RTO ≤ 24h, RPO ≤ 4h)
  ├── Internal tooling and collaboration data
  ├── Development and test environment state
  ├── Historical reporting data
  └── Cold-path analytics data

3.2 Minimum Backup Frequency

The policy must specify minimum frequencies per tier. The ESA Joint Guidelines (JC 2023/83) on ICT risk management provide implicit guidance through the RTO/RPO framework established under Art.11. A policy that states "daily backups" for all data without distinguishing criticality does not satisfy Art.12(1)(a) — it must be frequency-differentiated:

TierBackup TypeMinimum FrequencyMethod
Tier 1Continuous replicationReal-time / ≤ 15 min lagCDC, WAL streaming, synchronous replication
Tier 1Point-in-time snapshotEvery 4 hoursVolume snapshots, database dump
Tier 2Incremental backupEvery 1 hourBlock-level incremental
Tier 2Full backupDailyEncrypted off-site
Tier 3IncrementalDailyStandard incremental
Tier 3Full backupWeeklyCompressed, encrypted

3.3 Restoration and Recovery Procedures

Art.12(1)(b) requires documented restoration and recovery procedures. This must be operational documentation — not high-level BCP narrative — that a trained engineer can execute under stress:

RESTORATION PROCEDURE TEMPLATE (Art.12(1)(b) Evidence)

Procedure ID: RESTORE-CORE-DB-001
Tier: 1 (Critical)
Target RTO: 2 hours | Target RPO: 15 minutes

STEP 1 — TRIGGER (0-5 min)
  [ ] Incident declared by CISO or deputy
  [ ] Art.19 major incident assessment completed (if applicable)
  [ ] Restore team assembled (DBA + Network + App owner)

STEP 2 — BACKUP SELECTION (5-15 min)
  [ ] Identify last known-good snapshot timestamp
  [ ] Verify backup integrity hash (SHA-256 check)
  [ ] Confirm backup is NOT on same storage as compromised system
  [ ] Document selected backup: [timestamp] [location] [hash]

STEP 3 — RESTORE ENVIRONMENT ACTIVATION (15-45 min)
  [ ] Activate pre-provisioned standby environment (physically segregated)
  [ ] Verify network isolation from source system
  [ ] Confirm no shared credentials between source and restore target

STEP 4 — DATA RESTORATION (45-90 min)
  [ ] Mount encrypted backup volume (decrypt with HSM — NOT software key)
  [ ] Execute restore script: restore.sh --target=DR --timestamp=[T]
  [ ] Validate restored row count vs last consistent checkpoint
  [ ] Run integrity checks: checksum_verify.py --db=restored

STEP 5 — VALIDATION AND CUTOVER (90-120 min)
  [ ] Smoke test critical business functions
  [ ] Authorise cutover: [dual sign-off required]
  [ ] Update DNS / load balancer routing
  [ ] Notify Art.19 competent authority if incident reportable

Recovery Owner: [DBA Lead]
Backup Recovery Trainer: reviewed quarterly
Last test date: [DATE] | Test result: [PASS/FAIL]

4. Art.12(2): Backup System Activation Without Security Degradation

Art.12(2) adds a constraint that is frequently overlooked: backup systems must be activatable without compromising the security of the network and information systems, or the availability, authenticity, integrity, or confidentiality of data.

4.1 The Security-Continuity Tension

In practice, this requirement prohibits several common "fast restore" shortcuts:

ShortcutArt.12(2) Violation
Restoring to production environment while incident is still activeExposes restored data to ongoing attacker
Using shared credentials between production and backup systemsSingle compromised secret = backup access loss
Disabling encryption during restore for speedConfidentiality violation during transit
Bypassing MFA on restore console "in emergency"Authentication integrity degraded
Restoring database over same IP/hostname without network changeAvailability risk: DNS / routing conflicts

4.2 Periodic Testing Requirement

Art.12(2) explicitly requires periodic testing of backup procedures and restoration methods. "Periodic" is not defined in the regulation, but NCA examination practice and the ESA supervisory convergence framework treat the following as a minimum:

Test TypeMinimum FrequencyEvidence
Backup integrity check (automated)Weekly (Tier 1), Monthly (Tier 2-3)Automated hash verification log
Table-top restore exerciseQuarterlyMinutes, timeline, findings
Full failover test (production-equivalent)AnnuallyTest report signed by management
Cross-region restore validationAnnuallyRestore from alternate location, confirmed

5. Art.12(3): Physical and Logical Segregation of Restore Systems

Art.12(3) is among the most technically specific provisions in DORA Chapter II. When restoring backup data using own systems, financial entities must use ICT systems that are:

5.1 What "Physical and Logical Segregation" Means

The dual requirement creates a two-layer separation mandate:

SEGREGATION ARCHITECTURE (Art.12(3) Evidence)

PRODUCTION ENVIRONMENT (Source — potentially compromised)
  ├── prod-db-01 (PostgreSQL primary)
  ├── prod-app-01..04 (application servers)
  ├── prod-lb-01 (load balancer)
  └── prod-net: 10.0.1.0/24

         ↕ NO DIRECT NETWORK CONNECTIVITY ↕
         (separate VPC / vNet / VLAN required)

DR RESTORE ENVIRONMENT (Target — Art.12(3) compliant)
  ├── dr-db-01 (PostgreSQL restore target)
  ├── dr-app-01..02 (scaled-down application layer)
  ├── dr-lb-01 (independent load balancer)
  └── dr-net: 10.1.1.0/24 (separate subnet)
  
SEPARATION CONTROLS:
  ├── Physical: separate server rack OR separate cloud region
  ├── Logical: separate VPC/vNet with no peering to production
  ├── Credential: separate IAM roles, separate secrets vault instance
  ├── DNS: separate zone (dr.example.com vs. example.com)
  └── Certificate: separate TLS certificates (not shared with prod)

5.2 Cloud-Native Implementation

For entities using cloud infrastructure, Art.12(3) can be satisfied through:

  1. Multi-region deployment: Production in eu-west-1 (Frankfurt), DR in eu-central-1 (Dublin). Separate VPC per region. No VPC peering between prod and DR.

  2. Separate cloud accounts: Production in Account A, DR restore in Account B. No cross-account IAM trust. Backup copied via encrypted S3 cross-account replication with separate CMK per account.

  3. Air-gapped backup vault: Production and backup vault accessible through separate IAM identities. Vault uses Write-Once Read-Many (WORM) policy to prevent ransomware deletion.

Critical: Storing backups in the same cloud account as production does not satisfy Art.12(3) segregation if a compromised IAM role can reach both. The separation must be enforced at the account or identity boundary level.


6. Art.12(4): Geographic Location Segregation

Art.12(4) requires backup copies to be held at a location segregated from the primary location. The regulation offers two compliant paths:

(a) Other premises — at an appropriate distance to escape damage, with a different risk profile than the primary location

(b) Contracted cloud services — explicitly permitted as an Art.12(4) compliant location

6.1 "Appropriate Distance" and "Different Risk Profile"

The regulation does not specify kilometres. NCA guidance and the ESA supervisory convergence framework interpret "appropriate distance" through a risk-profile lens: the backup location must not share the physical threat profile of the primary site. This means:

Risk to AvoidImplication
Same building / data centreNo — same fire compartment, same power grid
Same city / urban areaBorderline — same flood plain, same seismic zone
Same country, different cityAcceptable for most entities
Different EU Member StatesPreferred for critical entities
Same cloud region but different AZNOT acceptable — same regional disaster risk
Different cloud regions (intra-EU)Acceptable under Art.12(4)(b)

Rule of thumb for cloud entities: Primary in eu-west-1 (Frankfurt) and backup in eu-central-1 (Dublin) or eu-south-1 (Milan) satisfies the "different risk profile" requirement. Primary in eu-west-1 and backup in eu-west-1 AZ-b does not — same regional infrastructure event can affect both.

6.2 The 3-2-1-1-0 Backup Architecture

DORA Art.12 combined with Art.11 RTO/RPO requirements effectively mandates an architecture that maps closely to the 3-2-1-1-0 backup rule extended for ransomware resilience:

3-2-1-1-0 BACKUP ARCHITECTURE (Art.12 Aligned)

3 COPIES of data
  ├── Copy 1: Production (live data)
  ├── Copy 2: Near-line backup (same region, different AZ, immutable)
  └── Copy 3: Off-site backup (different region / premises)

2 DIFFERENT MEDIA TYPES
  ├── Block storage (SSD/NVMe) for operational data
  └── Object storage (S3-compatible, WORM) for backup copies

1 COPY OFF-SITE (Art.12(4) geographic segregation)
  └── Different cloud region OR separate physical premises

1 COPY AIR-GAPPED (Ransomware protection)
  └── Write-Once Read-Many (WORM) bucket — delete protection enabled
      └── Separate IAM identity — NOT accessible from production IAM role

0 ERRORS on restore
  └── Verified by periodic integrity checks + annual full restore test

Why "0 errors": Art.12(2) requires periodic testing. A backup that has never been fully restored is an Art.12(2) violation. The "0 errors" principle means every backup tier is tested to restoration — not just hash-verified.


7. Python DORABackupChecker Implementation

The following implementation provides a self-assessment tool that checks all four Art.12 paragraphs. Integrate it into your compliance pipeline to generate audit evidence:

from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
import json
from datetime import datetime, timedelta


class CriticalityTier(Enum):
    TIER_1_CRITICAL = "tier_1_critical"
    TIER_2_IMPORTANT = "tier_2_important"
    TIER_3_STANDARD = "tier_3_standard"


class BackupType(Enum):
    CONTINUOUS_REPLICATION = "continuous_replication"
    POINT_IN_TIME_SNAPSHOT = "point_in_time_snapshot"
    INCREMENTAL = "incremental"
    FULL = "full"


class StorageLocation(Enum):
    SAME_ACCOUNT_SAME_REGION = "same_account_same_region"
    SAME_ACCOUNT_DIFFERENT_AZ = "same_account_different_az"
    SEPARATE_ACCOUNT_SAME_REGION = "separate_account_same_region"
    SEPARATE_ACCOUNT_DIFFERENT_REGION = "separate_account_different_region"
    ON_PREMISES_SEPARATE_SITE = "on_premises_separate_site"


@dataclass
class BackupDataCategory:
    name: str
    tier: CriticalityTier
    backup_frequency_minutes: int
    last_backup_timestamp: Optional[datetime]
    backup_location: StorageLocation
    is_encrypted: bool
    integrity_check_frequency_days: int
    last_integrity_check: Optional[datetime]
    is_worm_protected: bool
    restore_tested: bool
    last_restore_test: Optional[datetime]


@dataclass
class RestoreEnvironment:
    name: str
    physically_segregated: bool
    logically_segregated: bool
    separate_credentials: bool
    separate_network_segment: bool
    geographic_region: str
    production_region: str


@dataclass
class DORABackupCheckerResult:
    article_121_compliant: bool
    article_122_compliant: bool
    article_123_compliant: bool
    article_124_compliant: bool
    overall_compliant: bool
    findings: list[str]
    score: int
    evidence_items: list[str]


class DORABackupChecker:
    """
    Self-assessment tool for DORA Article 12 compliance.
    Generates structured findings and evidence trail for NCA audits.
    """

    # Minimum backup frequencies by tier (minutes)
    TIER_1_CONTINUOUS_MAX_LAG = 15
    TIER_1_SNAPSHOT_MAX_INTERVAL = 240
    TIER_2_INCREMENTAL_MAX_INTERVAL = 60
    TIER_3_INCREMENTAL_MAX_INTERVAL = 1440  # 24h

    # Test frequency requirements (days)
    TIER_1_INTEGRITY_CHECK_MAX_DAYS = 7
    TIER_2_INTEGRITY_CHECK_MAX_DAYS = 30
    FULL_RESTORE_TEST_MAX_DAYS = 365

    def __init__(
        self,
        data_categories: list[BackupDataCategory],
        restore_environment: RestoreEnvironment,
        backup_policy_documented: bool,
        recovery_procedures_documented: bool,
        last_full_restore_test: Optional[datetime],
        last_tabletop_exercise: Optional[datetime],
    ):
        self.data_categories = data_categories
        self.restore_env = restore_environment
        self.backup_policy_documented = backup_policy_documented
        self.recovery_procedures_documented = recovery_procedures_documented
        self.last_full_restore_test = last_full_restore_test
        self.last_tabletop_exercise = last_tabletop_exercise
        self.findings: list[str] = []
        self.evidence_items: list[str] = []

    def check_article_121(self) -> bool:
        """Art.12(1): Backup policies documented, scope defined, frequency by criticality."""
        passed = True

        if not self.backup_policy_documented:
            self.findings.append(
                "ART12_1_A: Backup policy not documented. "
                "Require: written policy specifying data scope and backup frequencies per criticality tier."
            )
            passed = False
        else:
            self.evidence_items.append("ART12_1_A: Backup policy document — PRESENT")

        if not self.recovery_procedures_documented:
            self.findings.append(
                "ART12_1_B: Restoration and recovery procedures not documented. "
                "Require: step-by-step operational runbooks per data tier."
            )
            passed = False
        else:
            self.evidence_items.append("ART12_1_B: Recovery procedure runbooks — PRESENT")

        for cat in self.data_categories:
            if cat.tier == CriticalityTier.TIER_1_CRITICAL:
                if cat.backup_frequency_minutes > self.TIER_1_SNAPSHOT_MAX_INTERVAL:
                    self.findings.append(
                        f"ART12_1_FREQ: {cat.name} (Tier 1 Critical): backup frequency "
                        f"{cat.backup_frequency_minutes} min exceeds 240 min maximum. "
                        "Increase snapshot frequency or add continuous replication."
                    )
                    passed = False
            elif cat.tier == CriticalityTier.TIER_2_IMPORTANT:
                if cat.backup_frequency_minutes > self.TIER_2_INCREMENTAL_MAX_INTERVAL:
                    self.findings.append(
                        f"ART12_1_FREQ: {cat.name} (Tier 2 Important): backup frequency "
                        f"{cat.backup_frequency_minutes} min exceeds 60 min maximum."
                    )
                    passed = False

        return passed

    def check_article_122(self) -> bool:
        """Art.12(2): Backup systems activate without security degradation; periodic testing."""
        passed = True
        now = datetime.utcnow()

        for cat in self.data_categories:
            if not cat.is_encrypted:
                self.findings.append(
                    f"ART12_2_ENC: {cat.name}: backup copies not encrypted. "
                    "Activation without encryption degradation: all backup I/O must be encrypted in transit and at rest."
                )
                passed = False

            max_check_days = (
                self.TIER_1_INTEGRITY_CHECK_MAX_DAYS
                if cat.tier == CriticalityTier.TIER_1_CRITICAL
                else self.TIER_2_INTEGRITY_CHECK_MAX_DAYS
            )

            if cat.last_integrity_check is None:
                self.findings.append(
                    f"ART12_2_INTEG: {cat.name}: no integrity check recorded. "
                    "Art.12(2) requires periodic testing of backup procedures."
                )
                passed = False
            elif (now - cat.last_integrity_check).days > max_check_days:
                self.findings.append(
                    f"ART12_2_INTEG: {cat.name}: last integrity check {(now - cat.last_integrity_check).days} days ago "
                    f"(max {max_check_days} days for this tier). Schedule integrity verification."
                )
                passed = False
            else:
                self.evidence_items.append(
                    f"ART12_2_INTEG: {cat.name} integrity check — {cat.last_integrity_check.date()} PASS"
                )

        if self.last_full_restore_test is None:
            self.findings.append(
                "ART12_2_TEST: No full restore test ever conducted. "
                "Art.12(2) requires periodic testing of restoration procedures — annual minimum recommended."
            )
            passed = False
        elif (now - self.last_full_restore_test).days > self.FULL_RESTORE_TEST_MAX_DAYS:
            self.findings.append(
                f"ART12_2_TEST: Last full restore test {(now - self.last_full_restore_test).days} days ago. "
                "Conduct annual full restore test and generate signed test report."
            )
            passed = False
        else:
            self.evidence_items.append(
                f"ART12_2_TEST: Full restore test — {self.last_full_restore_test.date()} PASS"
            )

        return passed

    def check_article_123(self) -> bool:
        """Art.12(3): Restore systems physically and logically segregated from source."""
        passed = True

        if not self.restore_env.physically_segregated:
            self.findings.append(
                "ART12_3_PHYS: Restore environment not physically segregated from production. "
                "Require: separate rack, separate DC, or separate cloud region/account."
            )
            passed = False
        else:
            self.evidence_items.append("ART12_3_PHYS: Physical segregation — CONFIRMED")

        if not self.restore_env.logically_segregated:
            self.findings.append(
                "ART12_3_LOGIC: Restore environment not logically segregated. "
                "Require: separate VPC/vNet with no peering to production network."
            )
            passed = False
        else:
            self.evidence_items.append("ART12_3_LOGIC: Logical network segregation — CONFIRMED")

        if not self.restore_env.separate_credentials:
            self.findings.append(
                "ART12_3_CRED: Restore environment shares credentials with production. "
                "Require: separate IAM roles, separate secrets vault instance, no cross-trust."
            )
            passed = False
        else:
            self.evidence_items.append("ART12_3_CRED: Credential segregation — CONFIRMED")

        if not self.restore_env.separate_network_segment:
            self.findings.append(
                "ART12_3_NET: Restore environment shares network segment with production. "
                "Require: separate VLAN or subnet with firewall ACL blocking cross-segment traffic."
            )
            passed = False

        return passed

    def check_article_124(self) -> bool:
        """Art.12(4): Backup copies at geographically separate location."""
        passed = True
        non_compliant_locations = {
            StorageLocation.SAME_ACCOUNT_SAME_REGION,
            StorageLocation.SAME_ACCOUNT_DIFFERENT_AZ,
        }

        for cat in self.data_categories:
            if cat.backup_location in non_compliant_locations:
                self.findings.append(
                    f"ART12_4_GEO: {cat.name}: backup location '{cat.backup_location.value}' "
                    "does not satisfy Art.12(4) geographic segregation. "
                    "Require: separate account + different region, OR on-premises at separate site."
                )
                passed = False
            else:
                self.evidence_items.append(
                    f"ART12_4_GEO: {cat.name} backup location '{cat.backup_location.value}' — COMPLIANT"
                )

            if cat.tier == CriticalityTier.TIER_1_CRITICAL and not cat.is_worm_protected:
                self.findings.append(
                    f"ART12_4_WORM: {cat.name} (Tier 1): backup not WORM-protected. "
                    "Recommend: immutable storage (Write-Once Read-Many) to prevent ransomware deletion. "
                    "Not strictly required by Art.12 text but standard NCA expectation."
                )

        if self.restore_env.geographic_region == self.restore_env.production_region:
            self.findings.append(
                f"ART12_4_REGION: Restore environment in same region as production "
                f"({self.restore_env.production_region}). "
                "Different regions recommended to satisfy 'different risk profile' requirement."
            )

        return passed

    def assess(self) -> DORABackupCheckerResult:
        art121 = self.check_article_121()
        art122 = self.check_article_122()
        art123 = self.check_article_123()
        art124 = self.check_article_124()

        total_checks = 4
        passed_checks = sum([art121, art122, art123, art124])
        score = int((passed_checks / total_checks) * 100)

        return DORABackupCheckerResult(
            article_121_compliant=art121,
            article_122_compliant=art122,
            article_123_compliant=art123,
            article_124_compliant=art124,
            overall_compliant=all([art121, art122, art123, art124]),
            findings=self.findings,
            score=score,
            evidence_items=self.evidence_items,
        )


# Usage example
if __name__ == "__main__":
    categories = [
        BackupDataCategory(
            name="Core Transaction Database",
            tier=CriticalityTier.TIER_1_CRITICAL,
            backup_frequency_minutes=15,
            last_backup_timestamp=datetime.utcnow() - timedelta(minutes=12),
            backup_location=StorageLocation.SEPARATE_ACCOUNT_DIFFERENT_REGION,
            is_encrypted=True,
            integrity_check_frequency_days=7,
            last_integrity_check=datetime.utcnow() - timedelta(days=3),
            is_worm_protected=True,
            restore_tested=True,
            last_restore_test=datetime.utcnow() - timedelta(days=90),
        ),
        BackupDataCategory(
            name="Customer Identity Store",
            tier=CriticalityTier.TIER_2_IMPORTANT,
            backup_frequency_minutes=60,
            last_backup_timestamp=datetime.utcnow() - timedelta(minutes=45),
            backup_location=StorageLocation.SAME_ACCOUNT_SAME_REGION,  # NON-COMPLIANT
            is_encrypted=True,
            integrity_check_frequency_days=30,
            last_integrity_check=datetime.utcnow() - timedelta(days=20),
            is_worm_protected=False,
            restore_tested=False,
            last_restore_test=None,
        ),
    ]

    restore_env = RestoreEnvironment(
        name="DR Restore Environment — eu-central-1",
        physically_segregated=True,
        logically_segregated=True,
        separate_credentials=True,
        separate_network_segment=True,
        geographic_region="eu-central-1",
        production_region="eu-west-1",
    )

    checker = DORABackupChecker(
        data_categories=categories,
        restore_environment=restore_env,
        backup_policy_documented=True,
        recovery_procedures_documented=True,
        last_full_restore_test=datetime.utcnow() - timedelta(days=180),
        last_tabletop_exercise=datetime.utcnow() - timedelta(days=60),
    )

    result = checker.assess()
    print(json.dumps(
        {
            "overall_compliant": result.overall_compliant,
            "score": result.score,
            "art12_1": result.article_121_compliant,
            "art12_2": result.article_122_compliant,
            "art12_3": result.article_123_compliant,
            "art12_4": result.article_124_compliant,
            "findings": result.findings,
            "evidence_items": result.evidence_items,
        },
        indent=2
    ))

Running the example above produces findings including ART12_4_GEO: Customer Identity Store backup location 'same_account_same_region' does not satisfy Art.12(4) geographic segregation — a common gap in organisations that use a single cloud account for both production and backups.


8. DORA Art.12 × NIS2 Art.21(2)(c) — Dual Compliance Mapping

Entities subject to both DORA and NIS2 (i.e., financial entities that are also "essential entities" under NIS2 Annex I or II) face overlapping backup and continuity requirements. DORA Art.12 operates as lex specialis — it takes precedence over NIS2 Art.21(2)(c) for in-scope financial entities:

RequirementDORA Art.12NIS2 Art.21(2)(c)Dual-Entity Approach
Backup policy documentationArt.12(1)(a): required, frequency by criticalityArt.21(2)(c): "backup management and recovery"Single policy document satisfies both — include NIS2 reference
Restoration proceduresArt.12(1)(b): documented proceduresArt.21(2)(c): recovery procedures impliedSingle runbook library — cross-reference both legal bases
TestingArt.12(2): periodic testing requiredArt.21(2)(c): testing implied in "continuity management"DORA testing schedule satisfies NIS2; document NIS2 basis in test plan
Geographic segregationArt.12(4): explicit other-premises or cloudNIS2: not explicit — "backup management" is generalDORA Art.12(4) is stricter; full DORA compliance covers NIS2
Incident notificationArt.19: separate major incident reportingArt.23: 24h early warningDORA Art.19 is the primary obligation; Art.23 report filed concurrently if dual-regulated
Micro-enterprise exemptionArt.12(5): basic third-party solutionNIS2: SME threshold in Art.2Separate thresholds — verify both independently

Practical note: A single NCA audit finding under DORA Art.12 — for example, same-region backups — will also typically constitute a NIS2 Art.21(2)(c) finding if the entity is dual-regulated. A single remediation fixes both.


9. Common NCA Audit Failures Under Art.12

Based on supervisory guidance from BaFin, DNB, and the EBA Peer Review on ICT risk supervision (2024), the following failures appear most frequently in Art.12 examination findings:

  1. Backups in the same cloud account as production: The most common failure for cloud-native financial entities. A compromised production IAM role that can also delete backup buckets fails both Art.12(3) (credential segregation) and Art.12(4) (geographic segregation intent).

  2. No documented data scope: Backup policy exists, but it covers "databases" without specifying which databases, which tables, and which criticality tier applies to each. Art.12(1)(a) requires explicit scope.

  3. Restore never tested end-to-end: Hash checks are automated, but the last actual restore from backup was performed years ago or never. Art.12(2) periodic testing requires documented full restore attempts, not just checksum verification.

  4. Shared restore network segment: DR environment exists, but it peers directly with the production VPC for operational convenience. This breaks Art.12(3) logical segregation — an attacker in production can reach the DR environment over the peering link.

  5. Same-AZ "off-site" backup: Backups stored in a different Availability Zone within the same cloud region are presented as Art.12(4) compliant. Different AZs within the same region share the same regional incident risk — NCAs do not accept this as geographic segregation.

  6. No cryptographic key separation: Production data encrypted with Key A. Backup encrypted with the same Key A stored in the same KMS instance. A compromised KMS access = both production and backup data exposed. Art.12(3) requires separate credential/key material.

  7. Backup frequency not differentiated by criticality: A single "nightly backup" policy for all data, regardless of RTO/RPO tier classification. Art.12(1)(a) explicitly requires frequency to be "based on the criticality of information or the confidentiality level of the data."


10. DORA Art.12: 25-Item Compliance Checklist

Use this checklist as NCA audit preparation evidence. Each item maps to a specific paragraph and generates a traceable evidence reference.

Art.12(1): Backup Policy and Procedures

#CheckEvidence Reference
1Backup policy document exists and is approved by management body (Art.5 governance)Policy document + sign-off date
2Policy explicitly defines scope of data subject to backup (all critical business function data)Data classification register
3Policy specifies minimum backup frequency differentiated by data criticality tierFrequency table in policy
4Policy specifies minimum backup frequency differentiated by confidentiality levelConfidentiality classification in policy
5Restoration and recovery procedures documented as operational runbooks (not just BCP narrative)Runbook library reference
6Runbooks specify who activates restore, dual sign-off requirement, escalation pathRACI in runbooks
7Runbooks reference RTO/RPO targets consistent with Art.11 BCP policyCross-reference Art.11 BCP

Art.12(2): Backup System Activation and Testing

#CheckEvidence Reference
8Backup activation procedure documented: step-by-step without disabling security controlsActivation procedure document
9All backup copies encrypted in transit (TLS ≥ 1.2)TLS configuration evidence
10All backup copies encrypted at rest (AES-256 or equivalent)Storage encryption evidence
11Encryption keys for backup are separate from production encryption keysKMS / HSM configuration evidence
12Automated integrity checks scheduled and results logged (Tier 1: weekly, Tier 2: monthly)Integrity check log
13Quarterly table-top restore exercise conducted with documented findingsExercise minutes
14Annual full restore test performed (production-equivalent scale, separate environment)Test report signed by management
15Test findings from restore exercises tracked to remediationFindings tracker

Art.12(3): Segregation of Restore Systems

#CheckEvidence Reference
16DR restore environment physically segregated from production (separate rack, DC, or cloud region)Infrastructure diagram
17DR restore environment logically segregated (separate VPC/vNet, no peering to prod)Network diagram + firewall rules
18DR restore environment uses separate IAM/service accounts — no shared credentials with productionIAM role inventory
19Backup encryption keys held in separate KMS/HSM not accessible from production IAM contextKMS access policy evidence
20DR restore environment has no direct reachable path to production networkPenetration test or network ACL evidence

Art.12(4): Geographic Location Segregation

#CheckEvidence Reference
21At least one complete backup copy stored at location physically separate from primary siteLocation documentation (cloud region docs, DC contract, or equivalent)
22Off-site location has demonstrably different risk profile (different flood plain, seismic zone, power grid)Risk profile assessment
23If using contracted cloud: backup stored in different cloud region from productionCloud console evidence (region confirmation)
24Backup copy stored in separate cloud account from production (not same account)Cloud account structure evidence
25Tier 1 Critical backups protected with WORM / immutable storage policyBucket/storage policy evidence

11. Art.12 in the DORA Chapter II Sequence

Art.12 does not stand alone. It forms part of a logical chain within the ICT risk management framework:

DORA CHAPTER II — ICT RISK MANAGEMENT FRAMEWORK

Art.5-6  → GOVERNANCE + FRAMEWORK SCOPE
  └─ Who owns the backup policy? (Management body accountability)

Art.7    → ICT SYSTEMS, PROTOCOLS, TOOLS
  └─ What systems must be backed up? (Asset inventory basis for Art.12(1) scope)

Art.8    → IDENTIFICATION
  └─ Which assets are critical business functions? (CBF list drives Art.12 Tier 1)

Art.9    → PROTECTION AND PREVENTION
  └─ Encryption, access controls that also apply during backup/restore

Art.10   → DETECTION
  └─ What triggers a restore decision? (Detection of compromise → Art.12 activation)

Art.11   → RESPONSE AND RECOVERY (BCP)
  └─ What are the RTO/RPO targets? (Art.12 backup frequency must achieve Art.11 RPO)

Art.12   → BACKUP POLICIES + RESTORATION ← YOU ARE HERE
  └─ How are backups made, stored, segregated, and tested?

Art.13   → LEARNING AND EVOLVING
  └─ What do restore test findings feed back into backup policy improvement?

Understanding Art.12 as downstream of Art.8 (identification) and Art.11 (BCP targets) is critical for scoping. An entity that has not performed Art.8 critical function mapping cannot correctly scope its Art.12(1)(a) backup data categories — the two are logically linked.


12. 12-Week Implementation Timeline for Art.12 Gap Closures

For financial entities starting from a basic backup posture, the following 12-week timeline closes the most common Art.12 gaps:

WEEKS 1-2: DATA CLASSIFICATION AND POLICY
  [ ] Inventory all data assets (align with Art.8 CBF mapping)
  [ ] Assign criticality tiers to each data category
  [ ] Draft backup policy document (scope, frequency, confidentiality levels)
  [ ] Draft restoration runbooks for each Tier 1 critical data category
  [ ] Management body approval of policy and runbooks

WEEKS 3-4: ENCRYPTION AND KEY SEGREGATION
  [ ] Audit all backup encryption: in transit (TLS ≥ 1.2) and at rest (AES-256)
  [ ] Create separate KMS keys / HSM partition for backup encryption
  [ ] Revoke production IAM access to backup KMS keys
  [ ] Document key management in backup policy

WEEKS 5-7: GEOGRAPHIC SEGREGATION (Art.12(4))
  [ ] Identify current backup storage locations
  [ ] Provision backup storage in separate cloud region (or separate premises)
  [ ] Configure cross-region backup replication (encrypted, cross-account)
  [ ] Enable WORM / immutable policy on Tier 1 backup buckets
  [ ] Update network ACLs: block production IAM from backup buckets

WEEKS 8-9: RESTORE ENVIRONMENT SEGREGATION (Art.12(3))
  [ ] Provision DR restore environment in separate region / account
  [ ] Implement VPC/vNet isolation — no peering to production
  [ ] Create separate IAM roles for DR operations
  [ ] Deploy separate TLS certificate infrastructure in DR environment

WEEKS 10-11: TESTING AND EVIDENCE GENERATION
  [ ] Conduct first integrity check (all backup tiers)
  [ ] Conduct first full restore test from segregated backup
  [ ] Document test report with management sign-off
  [ ] Conduct table-top exercise using new runbooks

WEEK 12: AUDIT EVIDENCE PACKAGING
  [ ] Compile 25-item checklist evidence bundle
  [ ] Map all evidence items to Art.12(1)-(4) paragraphs
  [ ] Final management body review and sign-off
  [ ] Schedule recurring test calendar (quarterly table-tops, annual full restore)

13. Hosting Compliance Consideration: Cloud Act and Art.12(4)(b)

Art.12(4)(b) explicitly permits contracted cloud services as an Art.12(4) compliant backup location. This is notable because it acknowledges cloud as a legitimate geographic segregation mechanism — but it does not resolve the jurisdictional question raised by the US CLOUD Act (18 U.S.C. §2713).

Under the CLOUD Act, a US federal court can compel US-headquartered cloud providers (AWS, Azure, GCP) to produce data held by their European subsidiaries. For Tier 1 Critical data — transaction records, customer position data — this creates a conflict with GDPR Chapter V (international transfers) and DORA Art.30(2)(e) (data location and jurisdiction requirements in third-party contracts).

Practical consequence: An EU financial entity that stores Art.12(4)(b) backup copies on AWS eu-west-1 or Azure West Europe retains CLOUD Act exposure, since both are operated by US parent companies. The backup satisfies geographic segregation under Art.12(4)(b), but the jurisdictional risk under Art.30 and GDPR must be separately addressed.

EU-native infrastructure providers operating under EU jurisdiction and ownership — without US parent company exposure — eliminate this risk vector while simultaneously satisfying Art.12(4)(b) as contracted cloud services. For financial entities that have quantified the CLOUD Act risk in their Art.8 risk register, selecting an EU-native backup provider can reduce both Art.12 and Art.30 compliance surface area in a single infrastructure choice.


14. Key Takeaways

DORA Art.12 creates four distinct compliance obligations, each with a separate evidence trail:

  1. Art.12(1): Document a backup policy that explicitly names data scope, minimum frequencies per criticality tier, and operational restoration runbooks — not just a high-level BCP.

  2. Art.12(2): Test backup activation periodically. Automated hash checks satisfy integrity verification; annual full restore tests satisfy the restoration procedure testing requirement. Both are required.

  3. Art.12(3): Restore systems must be physically and logically separated from the source system. Same VPC, same account, or shared credentials do not satisfy this even if geographically distant.

  4. Art.12(4): Backup copies must be at a location with a different risk profile. Same cloud account in a different AZ is not sufficient. Different cloud region in a separate account, or on-premises at a separate site, satisfies the requirement.

The DORABackupChecker above provides a structured starting point for self-assessment and NCA evidence generation. Extend it with your organisation's specific data categories, RTO/RPO targets, and infrastructure topology.


See Also


Part of the sota.io EU Cyber Compliance Series — practical implementation guides for DORA, NIS2, GDPR, CRA, and the EU AI Act.