Database Encryption at Rest and In Transit

Khimananda Oli 9 min read Database
Database Encryption at Rest and In Transit

By Khimananda Oli | Last reviewed: August 2026

Securing sensitive data requires implementing database encryption at rest and in transit as a non-negotiable baseline for any production system handling PII, financial records, or health information. While many teams enable TLS on the application layer, they frequently leave the database connection unencrypted or storage volumes unprotected, creating critical gaps that fail SOC 2 and ISO 27001 audits. This guide provides the exact configurations and architectural patterns needed to close those gaps without sacrificing performance or operational visibility.

ApplicationClient / ORMIn Transit (TLS 1.3)Encrypted ChannelMutual Auth (mTLS)Certificate ValidationDatabase EnginePostgreSQL / MySQLAt Rest (AES-256)TDE / Volume EncryptionKMS / VaultKey ManagementDefense-in-Depth: Both layers must be active for compliance
Layered defense model for database encryption at rest and in transit showing TLS channel protection and KMS-managed storage encryption

How do you configure database encryption at rest and in transit for PostgreSQL?

PostgreSQL requires explicit configuration for both encryption layers; neither is enabled by default in most distributions. For database encryption at rest and in transit, you must address filesystem-level protection separately from network transport security. In practice, I recommend combining Transparent Data Encryption (TDE) via extensions like pg_tde for column/table-level granularity with full-disk encryption (LUKS or EBS encryption) as a defense-in-depth measure. For transit, enforce TLS 1.3 minimum and disable older protocol versions explicitly.

Enforcing TLS 1.3 connections

Edit your postgresql.conf to require SSL and specify strong ciphers. Never rely on client-side defaults:

# postgresql.conf
ssl = on
ssl_cert_file = '/etc/ssl/certs/server.crt'
ssl_key_file = '/etc/ssl/private/server.key'
ssl_ca_file = '/etc/ssl/certs/ca.crt'
ssl_min_protocol_version = 'TLSv1.3'
ssl_ciphers = 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256'

Then restrict pg_hba.conf to reject non-SSL connections entirely. Use hostssl instead of host for all remote entries:

# pg_hba.conf - Reject plaintext connections
hostssl all             all             0.0.0.0/0               scram-sha-256
hostssl replication     replicator      10.0.0.0/24             scram-sha-256
# Explicitly deny non-SSL (optional but recommended for audit clarity)
host    all             all             0.0.0.0/0               reject

Verify enforcement by connecting with psql "sslmode=require" and checking SELECT * FROM pg_stat_ssl;. Any connection showing ssl = false indicates a misconfiguration. For deeper context on securing PostgreSQL deployments, see the PostgreSQL administration essentials guide which covers complementary hardening steps.

Implementing storage encryption

For self-managed instances, use LUKS2 with AES-XTS-PLAIN64:

# Create encrypted volume (destructive - backup first!)
cryptsetup luksFormat --type luks2 --cipher aes-xts-plain64 \
  --key-size 512 --hash sha512 /dev/sdb
cryptsetup open /dev/sdb pgdata_encrypted
mkfs.ext4 /dev/mapper/pgdata_encrypted
mount /dev/mapper/pgdata_encrypted /var/lib/postgresql/data

On AWS RDS/Aurora, enable encryption at creation time using AWS KMS customer-managed keys (CMK). Note that you cannot encrypt an existing unencrypted RDS instance in-place; you must create an encrypted snapshot and restore from it. Always enable automated key rotation in KMS and restrict key policies to the RDS service principal only.

What is the difference between TDE and application-level encryption?

Understanding this distinction prevents costly architectural mistakes during database encryption at rest and in transit implementations. Transparent Data Encryption operates below the database engine, encrypting entire tablespaces or files automatically without schema changes. Application-level encryption encrypts specific fields before they reach the database, requiring code modifications but providing finer-grained control over what gets protected and who holds decryption keys.

CriteriaTransparent Data Encryption (TDE)Application-Level Encryption
Implementation EffortLow — config change or volume flagHigh — code changes, key management integration
Query PerformanceNear-zero overhead (hardware AES-NI)Significant — prevents indexing on encrypted columns
Search CapabilityFull — DB sees plaintext after decryptionLimited — requires blind indexing or deterministic encryption
Key SeparationDB admin can access decrypted dataApp holds keys; DB stores only ciphertext
Compliance FitSOC 2, ISO 27001 baselineHIPAA, PCI-DSS field-level requirements
Backup ProtectionEncrypted backups automaticBackups contain ciphertext regardless

In my experience helping Nepal-based fintech companies achieve compliance, TDE satisfies most audit requirements for general data protection. Reserve application-level encryption for highly regulated fields (credit card numbers, national IDs) where even DBA access must be prevented. Combining both approaches—TDE for bulk protection plus app-level for sensitive columns—provides the strongest posture but increases operational complexity significantly.

TDE Path (Transparent)ApplicationSends PlaintextDatabase EngineEncrypts on WriteEncrypted StorageAES-256 on DiskKMS KeyAuto-RotationApplication-Level Path (Explicit)ApplicationEncrypts Before SendApp Key StoreCiphertext OnlyDatabase EngineStores Opaque BytesEncrypted StorageDouble-ProtectedChoose based on threat model: TDE for infrastructure breaches, App-level for insider threats
Data flow comparison between TDE and application-level encryption strategies for database encryption at rest and in transit

How do you manage encryption keys for SOC 2 and ISO 27001 compliance?

Key management separates compliant database encryption at rest and in transit implementations from checkbox exercises. Auditors scrutinize key lifecycle controls more than the encryption algorithm itself. You must demonstrate separation of duties, automated rotation, and tamper-evident logging. Never store encryption keys on the same server as the database or in application config files.

  • Use managed KMS services (AWS KMS, Azure Key Vault, GCP Cloud KMS) for cloud workloads. These provide HSM-backed key storage, automatic rotation schedules, and CloudTrail/Audit Log integration out of the box.
  • Implement envelope encryption: KMS encrypts data encryption keys (DEKs), and DEKs encrypt actual data. This limits KMS API calls and allows efficient key rotation without re-encrypting terabytes of data.
  • Restrict key policies to least privilege. The database service should only have kms:Decrypt and kms:GenerateDataKey permissions, never kms:CreateKey or kms:DisableKey.
  • Automate rotation every 90 days minimum for SOC 2 Type II. Configure alerts for rotation failures—missed rotations are common audit findings.
  • Log all key usage and ship logs to immutable storage. Enable KMS key policy conditions requiring secure transport (aws:SecureTransport) to prevent accidental plaintext API calls.

For on-premises or hybrid environments in Nepal where cloud KMS isn't viable, HashiCorp Vault provides equivalent capabilities with self-hosted HSM integration. See the secrets management with HashiCorp Vault guide for deployment patterns that satisfy audit requirements without cloud vendor lock-in.

What are common mistakes when implementing database encryption at rest and in transit?

After reviewing dozens of production setups across AWS, Azure, and self-managed infrastructure, these errors appear repeatedly in database encryption at rest and in transit projects:

  1. Assuming cloud defaults are sufficient. AWS RDS encryption is opt-in at creation. Azure SQL TDE uses Microsoft-managed keys by default, which some auditors reject for high-sensitivity data. Always verify and document your key ownership model.
  2. Leaving localhost connections unencrypted. Many configs enforce TLS for remote hosts but allow plaintext on 127.0.0.1. Containerized apps connecting via localhost bypass transit encryption entirely. Use Unix sockets with proper permissions or enforce TLS even for loopback.
  3. Ignoring backup encryption. Your database may be encrypted, but if pg_dump or native backups write plaintext to S3/GCS, you've defeated the purpose. Configure backup encryption explicitly and test restoration quarterly.
  4. Certificate expiry causing outages. TLS certificates expire. Set up monitoring for certificate validity windows (alert at 30, 14, and 7 days). Automate renewal with cert-manager for Kubernetes or ACM for AWS. Manual certificate management doesn't scale.
  5. Performance testing only after go-live. Encryption adds CPU overhead. Benchmark with realistic loads before production. Modern CPUs with AES-NI handle TDE with <5% overhead, but application-level encryption on indexed columns can cause 10-50x query slowdowns. Test early.

A frequent oversight in Nepal's growing tech sector is neglecting data residency implications when using cloud KMS. If your data must remain within national borders per local regulations, ensure the KMS region matches your database region and document this in your compliance evidence. Cross-region key access can violate residency requirements even if the data itself never leaves the country.

Start: Define Threat ModelIs data subject to HIPAA/PCI/NRB?YESNOApp-Level + TDEField encryption for PII/payment dataCustomer-managed keys requiredTDE / Volume Encryption OnlyProtects against disk theft/leaksService-managed keys acceptableEnforce mTLS + Key Rotation 90dAudit logging mandatoryEnforce TLS 1.3 + Auto-RotationMonitor cert expiryValidate: Pen Test + Audit EvidenceDocument key custody, rotation logs, TLS config
Decision framework for choosing encryption depth based on regulatory requirements and threat models

How do you verify database encryption at rest and in transit is working correctly?

Configuration alone isn't proof. Auditors and penetration testers demand evidence that database encryption at rest and in transit functions as designed under real conditions. Build verification into your CI/CD pipeline and operational runbooks rather than treating it as a pre-audit scramble.

Transit verification commands

For PostgreSQL, query the SSL status view directly:

-- Check all active connections for encryption status
SELECT pid, usename, client_addr, ssl, version, cipher 
FROM pg_stat_ssl 
JOIN pg_stat_activity USING (pid)
WHERE ssl = false AND client_addr IS NOT NULL;

Any rows returned indicate unencrypted remote connections. Integrate this as a failing check in your Prometheus monitoring fundamentals setup by exporting it as a gauge metric. Alert immediately when the count exceeds zero.

For MySQL/MariaDB, run SHOW STATUS LIKE 'Ssl_cipher'; on each connection or query performance_schema.tls_channel_status in MySQL 8.0+. On the network level, use sshd -T | grep ciphers and nmap --script ssl-enum-ciphers -p 5432 db-host to validate that weak protocols are truly disabled.

At-rest verification

Confirm volume encryption with cloud CLI tools:

# AWS RDS
aws rds describe-db-instances \
  --db-instance-identifier mydb \
  --query 'DBInstances[0].{Encrypted:StorageEncrypted,KMS:KmsKeyId}'

# Verify EBS volume encryption for self-managed
aws ec2 describe-volumes \
  --volume-ids vol-0abc123 \
  --query 'Volumes[0].Encrypted'

For LUKS-encrypted self-managed volumes, run cryptsetup status pgdata_encrypted and verify the cipher matches your policy. Document these checks in your compliance evidence repository and automate execution weekly. Manual verification drifts; automated checks don't.

Implementing Database Encryption at Rest and In Transit Securely

Effective database encryption at rest and in transit combines correct technical configuration with disciplined key management and continuous verification. Start with TDE and TLS 1.3 as your baseline, add application-level encryption only where threat models demand it, and automate evidence collection from day one. Compliance isn't a feature you bolt on—it's an outcome of engineering rigor applied consistently.

If your team needs help designing encryption architecture that passes audits without slowing development, or if you're preparing for SOC 2 certification and want to avoid common encryption pitfalls, reach out to discuss your specific infrastructure. I've helped organizations across Nepal and globally implement encryption strategies that satisfy both auditors and performance requirements.

Frequently Asked Questions

Encryption at rest protects stored data files using algorithms like AES-256, while encryption in transit secures data moving between client and server via TLS 1.3. Both are required for comprehensive database security compliance in 2026 infrastructure deployments.

Yes, expect five to ten percent overhead on write-heavy workloads due to cryptographic operations. Read performance impact is usually negligible with modern hardware acceleration. Always benchmark your specific database engine and storage configuration before production deployment.

Generate certificates using certbot or internal PKI, then set require_secure_transport=ON in my.cnf. Configure clients to use ssl-mode=REQUIRED or VERIFY_IDENTITY. Test connectivity with mysql --ssl-mode=VERIFY_IDENTITY to ensure unencrypted connections are rejected properly.

No, native pgcrypto requires data migration. Use filesystem-level encryption like LUKS or cloud provider volume encryption for zero-downtime protection. Application-level column encryption allows granular control but requires schema changes and application code modifications for existing tables.

Use TLS 1.3 exclusively with AEAD ciphers like AES-256-GCM or ChaCha20-Poly1305. Disable TLS 1.2 and older protocols entirely. Configure your database and load balancer to reject weak ciphers and enforce perfect forward secrecy for all connections.

Managed encryption satisfies technical safeguards but you must still implement access controls, audit logging, and key management policies. Verify your provider's BAA covers encryption key handling. Customer-managed keys often provide additional assurance for regulated healthcare data environments.

Use envelope encryption with a KMS to rotate master keys without re-encrypting data. Schedule rotations quarterly or after personnel changes. Test restoration procedures before rotating production keys and maintain versioned key history for disaster recovery scenarios.

No. Encryption protects stored data from physical theft or unauthorized disk access only. SQL injection exploits application logic regardless of encryption status. Implement parameterized queries, input validation, and WAF rules separately from your encryption strategy.

Use nmap ssl-enum-ciphers for transit verification and check database system views like pg_stat_ssl or SHOW STATUS LIKE 'Ssl_cipher'. For at-rest encryption, inspect volume metadata in cloud consoles or run lsblk -o NAME,FSTYPE,TYPE on Linux hosts.

Database-level encryption is simpler and covers all data automatically. Application-level encryption provides field-specific control and protects against compromised database credentials. Choose based on threat model: use both for high-security environments requiring defense in depth.

Encrypted backups take ten to twenty percent longer due to compression inefficiency on ciphertext. Restore times increase similarly. Ensure backup storage has adequate IOPS and test restore SLAs regularly. Consider streaming encryption to reduce temporary unencrypted data exposure.

Data becomes permanently unrecoverable. Store master keys in a dedicated KMS with multi-region replication and strict access policies. Never store keys alongside encrypted data. Maintain offline key escrow procedures tested annually for disaster recovery compliance.

Technically yes, but not recommended. Let's Encrypt certificates expire every ninety days requiring frequent rotation. Use internal PKI or cloud-managed certificates with longer validity periods. Automate renewal with cert-manager or equivalent tooling to prevent connection failures.

Only in Enterprise Edition and Atlas. Community Edition requires filesystem encryption like LUKS or EBS encryption. Enable WiredTiger encryption explicitly with encryptionKeyFile parameter. Verify encryption status using db.adminCommand({getCmdLineOpts:1}) and check for encrypted storage engine configuration.

Check certificate chain validity, hostname matching, and protocol version compatibility using openssl s_client. Review database logs for specific error codes. Verify firewall rules allow TLS traffic and confirm client and server share at least one common cipher suite.