कई प्रदाताओं में अत्यधिक उपलब्ध nextxch
जब कोई पूरा प्रदाता ऑफ़लाइन हो जाए, तब भी मेल चलती रहनी चाहिए: महँगे anycast के बिना, एंटरप्राइज़ लोड बैलेंसर के बिना, Git से डिप्लॉय करने योग्य। यह गाइड Hetzner, netcup और STRATO के कम लागत वाले सर्वरों पर तीन सेटअप दिखाती है, सेकंड-दर-सेकंड बताती है कि आउटेज के दौरान क्या होता है, और संसाधनों तथा लागत की गणना करती है। प्रदाता के हर दावे के साथ जाँच की तिथि सहित स्रोत का लिंक है, nextxch के हर दावे के साथ कोड में स्रोत फ़ाइल का नाम दिया गया है।
01 · उच्च उपलब्धता के लिए nextxch क्या प्रदान करता है (कोड में जाँचा गया)
nextxch active-active सक्षम है: हर नोड SMTP, IMAP, JMAP, ActiveSync और HTTPS स्वीकार करता है, कोई निष्क्रिय नोड और कोई स्टिकी सत्र नहीं है। PostgreSQL साझा स्थिति रखता है, संदेश स्वयं ब्लॉब स्टोर में SHA-256-एड्रेस्ड .eml ऑब्जेक्ट के रूप में संग्रहीत होते हैं।
| क्षमता | कैसे | कोड में स्रोत |
|---|---|---|
| Redis के बिना क्लस्टर बैकप्लेन | नए मेल, फ़्लैग, expunge, कतार जैसे ईवेंट के लिए PostgreSQL LISTEN/NOTIFY (चैनल nextxch_cluster_events); UNLOGGED टेबल cluster_ephemeral और cluster_nodes; हर 15 s पर हार्टबीट, 60 s तक हार्टबीट न मिलने पर नोड हटा दिए जाते हैं | src/Nextxch.Storage/Cluster/PostgresClusterCoordinator.cs |
| लीडर कार्य | pg_try_advisory_lock: ठीक एक नोड ACME प्रमाणपत्र नवीनीकृत करता है, लाइसेंस हार्टबीट भेजता है, LDAP सिंक करता है, कैलेंडर रिमाइंडर भेजता है | AcmeCertificateManager.cs, ClusterLicenseService.cs, LdapSyncService.cs, CalendarReminderPushService.cs |
| नोड्स के पार SMTP कतार | FOR UPDATE SKIP LOCKED: हर नोड अलग कतार आइटम लेता है, कोई दोहरी डिलीवरी नहीं | src/Nextxch.Storage/Postgres/PostgresMailStore.cs |
| क्लस्टर-सक्षम डेटाबेस | PostgreSQL (क्लस्टर के लिए डिफ़ॉल्ट) और SQL Server (sp_getapplock); SQLite केवल सिंगल नोड | src/Nextxch.Storage/StorageServiceCollectionExtensions.cs |
| ब्लॉब स्टोर | FileSystem, S3 (Content-MD5 के साथ अपना SigV4 क्लाइंट) या Hybrid (लोकल NVMe कैश + S3)। Object Lock मोड Normal, Governance, Compliance और legal hold | src/Nextxch.Storage/Blobs/S3BlobStore.cs, HybridBlobStore.cs |
| प्रमाणपत्र और DKIM | ACME प्रमाणपत्र और DKIM कुंजियाँ डेटाबेस में रहती हैं; HTTP-01 चैलेंज पूरे क्लस्टर में साझा होते हैं, ताकि कोई भी नोड उत्तर दे सके; नवीनीकरण पर लीफ़ कुंजी बनी रहती है (DANE 3 1 1 मान्य रहता है) | src/Nextxch.Security/Tls/AcmeCertificateManager.cs |
| हेल्थ एंडपॉइंट | /health/live (DB नहीं), /health/ready (DB पहुँच योग्य, अन्यथा 503), /health (क्लस्टर पीयर सहित अवलोकन) | src/Nextxch.Server/Program.cs |
| वास्तविक क्लाइंट IP | SMTP, IMAP, ManageSieve के लिए PROXY protocol v1/v2 (Cluster:EnableProxyProtocol) और TrustedProxyCidrs से HTTP के लिए X-Forwarded-For | src/Nextxch.Core/Abstractions/IClusterCoordinator.cs |
| आर्टिफ़ैक्ट परिनियोजित करें | docker-compose.yml, Kubernetes मैनिफ़ेस्ट (3 रेप्लिका, HPA 2 से 10, PDB, probes), Helm chart, deb/rpm, Windows MSI | deploy/kubernetes/, deploy/helm/nextxch/, deploy/packages/ |
| लाइसेंस | सक्रिय मेलबॉक्स गिने जाते हैं, नोड या पॉड नहीं; लाइसेंस की स्थिति cluster_kv के माध्यम से साझा की जाती है | src/Nextxch.Server/Services/ClusterLicenseService.cs, मूल्य |
प्रति नोड पोर्ट: 25, 465, 587 (SMTP), 143, 993 (IMAP), 4190 (ManageSieve), 80/443 (HTTPS, ACME, वेबमेल, ActiveSync, JMAP)। POP3 110/995 डिफ़ॉल्ट रूप से बंद है (Pop3Settings.cs में Pop3:Enabled=false)।
/health/ready 503 लौटाता है और नोड प्राप्तकर्ताओं को सत्यापित नहीं कर सकता। इसलिए प्रदाताओं के पार उच्च उपलब्धता का अर्थ है PostgreSQL और ब्लॉब स्टोर को साइटों के पार रेप्लिकेट करना और स्वचालित रूप से स्विच करना। तीनों सेटअप यही बनाते हैं।02 · मेल-विशिष्ट नियम जो उच्च उपलब्धता को आसान बनाते हैं
इनबाउंड: MX में फेलओवर अंतर्निहित है
प्रेषक MX रिकॉर्ड को प्राथमिकता के अनुसार क्रमबद्ध करते हैं और उन्हें हर पते को आज़माना होता है; समान प्राथमिकता होने पर उन्हें क्रम को अनिवार्य रूप से यादृच्छिक करना होता है। [1] यदि कोई लक्ष्य पहुँच से बाहर है, तो पुनः प्रयास अंतराल कम से कम 30 मिनट और छोड़ने का समय कम से कम 4 से 5 दिन होना चाहिए। [1]
आउटबाउंड: प्रति IP SPF और PTR
SPF में हर साइट का हर आउटबाउंड IP सूचीबद्ध होना चाहिए; ip4/ip6, 10 DNS लुकअप की सीमा में नहीं गिने जाते। [2] Gmail को मान्य फ़ॉरवर्ड और रिवर्स DNS चाहिए; PTR प्रत्येक प्रदाता पर अलग से सेट किया जाता है। [8][14][27][31]
DKIM, MTA-STS, DANE
DKIM कुंजियाँ क्लस्टर डेटाबेस में रहती हैं, एक DNS सेट सभी नोड के लिए काम करता है; सेलेक्टर कई कुंजियों को समानांतर रूप से अनुमति देते हैं। [3] MTA-STS नीति में हर MX होस्ट का नाम होना चाहिए; वाइल्डकार्ड केवल सबसे बाएँ लेबल को कवर करते हैं। [4] DANE को प्रति MX होस्ट एक TLSA रिकॉर्ड चाहिए, 3 1 1 अनुशंसित है, और एक DNSSEC-हस्ताक्षरित ज़ोन। [5]
; Zone example.com (TTL in seconds). IPs are documentation ranges: replace them.
; Site A = Hetzner 192.0.2.10, site B = netcup 198.51.100.20, site C (medium setup) = 203.0.113.30
; Inbound SMTP: equal preference, senders MUST randomize (RFC 5321 section 5.1).
example.com. 3600 IN MX 10 mx1.example.com.
example.com. 3600 IN MX 10 mx2.example.com.
; Static per-site names: never touched by the failover job (stable FCrDNS for PTR).
mx1.example.com. 3600 IN A 192.0.2.10
mx2.example.com. 3600 IN A 198.51.100.20
; Client name (IMAP, submission, HTTPS, webmail, ActiveSync, autodiscover):
; short TTL, kept up to date by the health-check job. Start with all healthy nodes.
mail.example.com. 60 IN A 192.0.2.10
mail.example.com. 60 IN A 198.51.100.20
; SPF: every outbound IP of every site. ip4/ip6 do not count against the 10-lookup limit (RFC 7208 4.6.4).
example.com. 3600 IN TXT "v=spf1 ip4:192.0.2.10 ip4:198.51.100.20 ip4:203.0.113.30 -all"
; DKIM: nextxch keeps the keys in the cluster database, so one record set serves all nodes.
; Publish the selectors shown under /admin/ (Domains > DKIM).
; MTA-STS: the policy must list every MX host (see the note on gaps).
_mta-sts.example.com. 3600 IN TXT "v=STSv1; id=20261102"
; DANE (optional, needs a DNSSEC-signed zone): one TLSA record per MX host.
; nextxch reuses the leaf key on renewal, so "3 1 1" stays stable.
_25._tcp.mx1.example.com. 3600 IN TLSA 3 1 1 <sha256-of-spki>
_25._tcp.mx2.example.com. 3600 IN TLSA 3 1 1 <sha256-of-spki>
; PTR (set in the provider console, not in this zone):
; 192.0.2.10 -> mx1.example.com (Hetzner Console / Robot)
; 198.51.100.20 -> mx2.example.com (netcup SCP)
Smtp:HeloHostname या /admin/ में "Hostname, IPv6 & Ports" के अंतर्गत (प्रति नोड EHLO नाम, प्रति नोड PTR जाँच के साथ)। इसका उपयोग 220 बैनर, EHLO उत्तर और आउटबाउंड EHLO के लिए होता है। प्रत्येक IP का PTR उसके निश्चित नाम mx1/mx2 पर इंगित करें; DNS एडवाइज़र प्रति नोड एक HELO SPF रिकॉर्ड दिखाता है। RFC 5321 सेक्शन 4.1.4 वैसे भी किसी संदेश को केवल मेल न खाते EHLO नाम के कारण अस्वीकार करने से मना करता है। [1][8]# appsettings.Production.json per node (or admin: Hostname, IPv6 & Ports > EHLO name per node)
# Site A (Hetzner):
{ "Smtp": { "ServerHostName": "mail.example.com", "HeloHostname": "mx1.example.com" } }
# Site B (netcup):
{ "Smtp": { "ServerHostName": "mail.example.com", "HeloHostname": "mx2.example.com" } }
# PTR 192.0.2.10 -> mx1.example.com, PTR 198.51.100.20 -> mx2.example.com
# Served by nextxch itself at https://mta-sts.example.com/.well-known/mta-sts.txt
# (admin: Domains > domain > Transport > MTA-STS). Empty MX list = automatic:
# the domain's MX records from DNS + server host name + EHLO names of all nodes.
# Wildcards like *.example.com are allowed. Every change changes the policy id;
# the DNS advisor shows the matching _mta-sts TXT record.
version: STSv1
mode: enforce
mx: mx1.example.com
mx: mx2.example.com
max_age: 604800
03 · क्लाइंट कैसे पुनर्निर्देशित होते हैं: विकल्पों की तुलना
प्रदाता के फेलओवर IP केवल एक प्रदाता के भीतर मदद करते हैं। पूरे प्रदाता के आउटेज के लिए DNS सरल और सस्ता उपकरण बना रहता है।
| विकल्प | दायरा | लागत | आकलन |
|---|---|---|---|
| समान प्राथमिकता वाले कई MX | प्रदाताओं के पार | 0 € | इनबाउंड मेल के लिए अनिवार्य, मानकीकृत [1] |
| अपना हेल्थ-चेक जॉब + DNS API (Cloudflare, Hetzner, deSEC, netcup) | प्रदाताओं के पार | 0 € | अनुशंसित। Cloudflare: TTL 60 s से (Enterprise 30 s), auto = 300 s। Hetzner DNS: निःशुल्क, TTL 60 s से, Hetzner Console में API। deSEC: प्रति डोमेन न्यूनतम TTL, उदाहरण 3600 s, कम केवल अनुरोध पर। netcup: CCP DNS API मौजूद है, न्यूनतम TTL प्रलेखित नहीं। [38][39][21][40][29] |
| Cloudflare Load Balancing (केवल DNS) | प्रदाताओं के पार | USD 5/माह से | Enterprise के बाहर हर 15 s पर हेल्थ चेक (TCP, SMTP सहित); A/AAAA/CNAME के लिए, MX/SRV केवल Enterprise में। mail.example.com के लिए अपने जॉब की जगह लेता है। [33][34][35][36] |
| Cloudflare Spectrum | प्रदाताओं के पार | Enterprise | मनमाने TCP पोर्ट (25, 465, 587, 993) केवल Enterprise ऐड-ऑन के रूप में; Pro/Business में केवल SSH, Minecraft, RDP। [37] |
| Hetzner Floating IP (Cloud) | एक प्रोजेक्ट, एक नेटवर्क ज़ोन | IPv4 €3.00, IPv6 €1.00/माह | केवल Hetzner के भीतर [9] |
| Hetzner फेलओवर IP (Robot, डेडिकेटेड) | Hetzner डेटा सेंटर | €4.20/माह + €4.90 सेटअप | Robot API से 90 से 110 s में स्विच, केवल Hetzner के अंदर [11] |
| netcup फेलओवर IP | वही netcup स्थान | €5.00/माह, VAT सहित | SCP में कभी भी निःशुल्क पुनः रूट करें, केवल netcup के भीतर [24][25] |
| Akamai/Linode IP शेयरिंग | एक डेटा सेंटर, दो Linode | USD 5 से (Nanode) | BGP-आधारित (lelastic), केवल उसी डेटा सेंटर में; NodeBalancer Basic USD 10, TCP/HTTP [41][42] |
| Anycast / अपना BGP | global | उच्च | अपना ASN (सदस्यता या प्रायोजक LIR के माध्यम से RIPE अनुबंध) और अपना पता स्थान (Vultr पर कम से कम /24 IPv4 या /48 IPv6)। अधिकांश nextxch इंस्टॉलेशन के लिए अनुपातहीन। [43][44] |
04 · सेटअप S: दो साइटें और एक विटनेस
छोटी और मध्यम कंपनियों के लिए। साइट A (उदा. Hetzner Cloud) और साइट B (उदा. netcup VPS) दोनों पर nextxch, Patroni के अंतर्गत PostgreSQL, एक etcd सदस्य और एक Garage ज़ोन चलते हैं। किसी तीसरे प्रदाता (उदा. STRATO) पर एक छोटा विटनेस C तीसरा etcd सदस्य, तीसरी Garage प्रति और DNS फेलओवर चेकर रखता है। तीनों WireGuard से आपस में जुड़े (मेश) हैं।
flowchart TB
U["Users: IMAP, SMTP submission, HTTPS"] -->|"mail.example.com TTL 60"| DNS["DNS with health-check job"]
S["Remote mail servers"] -->|"MX 10 mx1 / MX 10 mx2"| A1
S --> B1
DNS --> A1
DNS --> B1
subgraph A["Site A: Hetzner"]
A1["nextxch"] --> AP[("PostgreSQL primary")]
A1 --> AG["Garage zone A"]
AE["etcd"]
end
subgraph B["Site B: netcup"]
B1["nextxch"] --> BP[("PostgreSQL replica")]
B1 --> BG["Garage zone B"]
BE["etcd"]
end
subgraph C["Witness C: STRATO"]
CE["etcd"]
CG["Garage zone C"]
CF["Failover checker"]
end
B1 -.->|"WireGuard"| AP
AP ==>|"synchronous replication"| BP
AG <-.-> BG
BG <-.-> CG
AE <-.-> BE
BE <-.-> CE
blob स्टोर के रूप में Garage क्यों?
एक ही प्रदाता पर S3 बकेट उस प्रदाता के साथ ही चला जाएगा, और nextxch वर्तमान में केवल एक S3 लक्ष्य पर लिख सकता है। Garage एक S3-संगत स्टोर (AGPL-3.0) है, जो इंटरनेट पर कई डेटा सेंटरों के लिए बनाया गया है (प्रोजेक्ट आवश्यकता: अधिकतम 200 ms लेटेंसी, कम से कम 50 Mbit/s)। replication_factor = 3 के साथ प्रति ज़ोन एक प्रति होती है; जब तक केवल एक ज़ोन विफल होता है, पढ़ना और लिखना जारी रहता है (राइट कोरम 2)। केवल दो ज़ोन होने पर एक ज़ोन खोने से स्टोर केवल-पठन रह जाएगा, इसीलिए विटनेस भी एक पूर्ण प्रति रखता है। [60][61]
चरण दर चरण
- सर्वर ऑर्डर करें: A = Hetzner Cloud (पोर्ट 25/465 डिफ़ॉल्ट रूप से ब्लॉक हैं; एक महीने और पहले इनवॉइस के भुगतान के बाद अनब्लॉक करने का अनुरोध करें, 587 खुला है), B = netcup VPS (SCP में फ़ायरवॉल नीति "netcup Mail block" हटाएँ), C = विटनेस के रूप में STRATO VPS (विटनेस को पोर्ट 25 की आवश्यकता नहीं है)। [13][26]
- PTR सेट करें: IP A को mx1.example.com (Hetzner Console), IP B को mx2.example.com (netcup SCP)। [14][27]
- cloud-init के माध्यम से बेस इंस्टॉल (Debian 13): deploy उपयोगकर्ता, Docker, WireGuard, पासवर्ड लॉगिन नहीं।
- A, B, C के बीच WireGuard मेश 10.88.0.0/24; UDP 51820 केवल पीयर IP के लिए खोलें। [47]
- तीनों पर etcd (election timeout कम से कम 10 × RTT), फिर A और B पर Patroni + PostgreSQL 18। [50][53]
- तीनों पर Garage, तीन ज़ोन वाला लेआउट लागू करें, बकेट और कुंजी बनाएँ। [61]
- A और B पर Docker Compose के माध्यम से nextxch (Cluster:Enabled=true, मल्टी-होस्ट कनेक्शन स्ट्रिंग, स्थानीय Garage की ओर इंगित S3)। /admin/ में होस्ट नाम mail.example.com सेट करें और mail, mx1, mx2 के लिए ACME प्रमाणपत्र जारी करें।
- DNS अध्याय 02 की तरह, /admin/ में MTA-STS MX सूची जाँचें, /admin/ में डोमेन के अंतर्गत DNS फेलओवर सक्षम करें (केवल क्लस्टर लीडर कार्य करता है) या वैकल्पिक रूप से etcd लॉक के अंतर्गत स्क्रिप्ट।
- अध्याय 09 के अनुसार बैकअप और एक रिस्टोर का परीक्षण करें। फिर एक आउटेज का अभ्यास करें: साइट A को हार्ड-स्टॉप करें और अध्याय 08 का पालन करें।
#cloud-config
# Hetzner Cloud / netcup: paste as user data when creating the VM.
users:
- name: deploy
groups: [sudo, docker]
shell: /bin/bash
sudo: "ALL=(ALL) NOPASSWD:ALL"
ssh_authorized_keys:
- ssh-ed25519 AAAA... ci-deploy
package_update: true
packages: [wireguard, curl, jq, docker.io, docker-compose-v2, unattended-upgrades]
runcmd:
- [ systemctl, enable, --now, docker ]
- [ sed, -i, "s/^#\\?PasswordAuthentication.*/PasswordAuthentication no/", /etc/ssh/sshd_config ]
- [ systemctl, restart, ssh ]
# /etc/wireguard/wg0.conf on site A (Hetzner). Same pattern on B and C.
# Full mesh: every site has one [Peer] block per other site.
[Interface]
Address = 10.88.0.1/24
ListenPort = 51820
PrivateKey = <private-key-of-site-A> # wg genkey, never commit it
[Peer] # site B (netcup)
PublicKey = <public-key-of-site-B>
Endpoint = 198.51.100.20:51820
AllowedIPs = 10.88.0.2/32
PersistentKeepalive = 25
[Peer] # site C (witness or third site)
PublicKey = <public-key-of-site-C>
Endpoint = 203.0.113.30:51820
AllowedIPs = 10.88.0.3/32
PersistentKeepalive = 25
# systemctl enable --now wg-quick@wg0
# Firewall: UDP 51820 open only for the two peer IPs.
# /etc/default/etcd on site A (Debian package etcd-server). Sites B and C analogue.
# Three members = quorum of two: any single site may fail.
ETCD_NAME="site-a"
ETCD_DATA_DIR="/var/lib/etcd/default"
ETCD_LISTEN_PEER_URLS="http://10.88.0.1:2380"
ETCD_LISTEN_CLIENT_URLS="http://10.88.0.1:2379,http://127.0.0.1:2379"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.88.0.1:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://10.88.0.1:2379"
ETCD_INITIAL_CLUSTER="site-a=http://10.88.0.1:2380,site-b=http://10.88.0.2:2380,site-c=http://10.88.0.3:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="nextxch-ha"
# WAN tuning (etcd tuning guide: election timeout at least 10x the round trip).
# Example for up to ~25 ms RTT between providers:
ETCD_HEARTBEAT_INTERVAL="250"
ETCD_ELECTION_TIMEOUT="2500"
# Traffic stays inside WireGuard. For production add TLS (ETCD_CERT_FILE etc.).
# /etc/patroni/config.yml on site A (Debian/PGDG package "patroni").
# Site B: name/addresses analogue. The witness C runs etcd only, no PostgreSQL.
scope: nextxch
name: pg-site-a
restapi:
listen: 10.88.0.1:8008
connect_address: 10.88.0.1:8008
etcd3:
hosts: 10.88.0.1:2379,10.88.0.2:2379,10.88.0.3:2379
bootstrap:
dcs:
ttl: 30 # defaults; rule: loop_wait + 2 * retry_timeout <= ttl
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
synchronous_mode: on # a 250 OK to a sender is only given after the commit reached a 2nd site
synchronous_mode_strict: false # if the replica site is gone, keep accepting mail (falls back to async)
failsafe_mode: true
postgresql:
use_pg_rewind: true
parameters:
wal_level: replica
hot_standby: "on"
max_wal_senders: 10
max_replication_slots: 10
wal_log_hints: "on"
archive_mode: "on"
archive_command: "pgbackrest --stanza=nextxch archive-push %p"
initdb: [ {encoding: UTF8}, data-checksums ]
postgresql:
listen: 10.88.0.1:5432
connect_address: 10.88.0.1:5432
data_dir: /var/lib/postgresql/18/main
bin_dir: /usr/lib/postgresql/18/bin
authentication:
replication: { username: replicator, password: "${PATRONI_REPLICATION_PASSWORD}" }
superuser: { username: postgres, password: "${PATRONI_SUPERUSER_PASSWORD}" }
pg_hba:
- host replication replicator 10.88.0.0/24 scram-sha-256
- host nextxch nextxch 10.88.0.0/24 scram-sha-256
# /etc/garage.toml, identical on all three sites except rpc_public_addr.
metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
db_engine = "lmdb"
replication_factor = 3 # one copy per zone (site); writes need 2 of 3
consistency_mode = "consistent"
rpc_bind_addr = "10.88.0.1:3901"
rpc_public_addr = "10.88.0.1:3901"
rpc_secret = "<openssl rand -hex 32, same on all nodes>"
[s3_api]
s3_region = "garage"
api_bind_addr = "127.0.0.1:3900" # each nextxch node talks to its local Garage
# After the first start (node ids from `garage node id`):
# garage layout assign <id-a> -z hetzner -c 500G
# garage layout assign <id-b> -z netcup -c 500G
# garage layout assign <id-c> -z witness -c 500G
# garage layout show && garage layout apply --version 1
# garage bucket create nextxch
# garage key create nextxch-app
# garage bucket allow --read --write --owner nextxch --key nextxch-app
# docker-compose.yml on site A and site B (nextxch only; PostgreSQL/Patroni, etcd,
# Garage and WireGuard run as system services on the same VM).
# Secrets come from .env (never committed): NEXTXCH_LICENSE, PG_PASSWORD, S3_KEY, S3_SECRET
name: nextxch
services:
nextxch:
image: nextxch/server:${NEXTXCH_TAG}
restart: unless-stopped
network_mode: host # real client IPs for SPF/DMARC, direct WireGuard reach
environment:
ASPNETCORE_URLS: "http://*:80;https://*:443"
License__Token: "${NEXTXCH_LICENSE}"
Cluster__Enabled: "true"
Cluster__NodeId: "${SITE_NAME}" # hetzner-a / netcup-b
Storage__Provider: "Postgres"
# Npgsql multi-host: always connects to the current Patroni primary
Storage__PostgresConnectionString: "Host=10.88.0.1,10.88.0.2;Port=5432;Database=nextxch;Username=nextxch;Password=${PG_PASSWORD};Target Session Attributes=primary;Host Recheck Seconds=5"
Storage__BlobBackend: "S3"
Storage__Compression: "Zstd"
Storage__S3__EndpointUrl: "http://127.0.0.1:3900"
Storage__S3__Region: "garage"
Storage__S3__BucketName: "nextxch"
Storage__S3__ForcePathStyle: "true"
Storage__S3__AccessKey: "${S3_KEY}"
Storage__S3__SecretKey: "${S3_SECRET}"
Smtp__ServerHostName: "mail.example.com"
Imap__ServerHostName: "mail.example.com"
ManageSieve__ServerHostName: "mail.example.com"
Acme__Enabled: "true"
Acme__ContactEmail: "postmaster@example.com"
volumes:
- nextxch-data:/app/data
healthcheck:
test: ["CMD", "curl", "-fsS", "http://127.0.0.1/health/ready"]
interval: 15s
timeout: 5s
retries: 3
volumes:
nextxch-data:
कनेक्शन स्ट्रिंग दोनों PostgreSQL होस्ट को Target Session Attributes=primary के साथ नामित करती है। nextxch अपने कनेक्शन NpgsqlDataSource.Create (PostgresMailStore.cs) के माध्यम से बनाता है; कई होस्ट होने पर Npgsql स्वचालित रूप से एक मल्टी-होस्ट डेटा स्रोत बनाता है और केवल वर्तमान प्राइमरी से कनेक्ट होता है, हर Host Recheck Seconds (डिफ़ॉल्ट 10 s) पर स्थिति की फिर से जाँच करता है। Patroni फेलओवर के बाद नोड बिना किसी कॉन्फ़िगरेशन परिवर्तन के फिर से कनेक्ट हो जाते हैं; क्लस्टर LISTEN कनेक्शन हर 2 s पर पुनः प्रयास करता है (PostgresClusterCoordinator.cs)। [63]
mail.example.com के लिए स्वचालित DNS फेलओवर
nextxch एक DNS फेलओवर एजेंट के साथ आता है (opt-in, डिफ़ॉल्ट रूप से बंद; /admin/ में डोमेन के अंतर्गत, कार्ड "DNS failover")। हर नोड हर 30 s में हर नोड के सार्वजनिक IP के माध्यम से पोर्ट 25 पर SMTP बैनर और HTTPS /health जाँचता है। केवल क्लस्टर लीडर कार्रवाई करता है: वह एक PostgreSQL advisory lock रखता है, सभी नोड के दृष्टिकोण को बहुमत से मिलाता है और कॉन्फ़िगर किए गए नामों के A/AAAA रिकॉर्ड Hetzner DNS (Cloud API) या Cloudflare API के माध्यम से लिखता है। हिस्टैरिसीस: N विफल राउंड के बाद नोड DNS से हटता है, M स्वस्थ राउंड के बाद वापस आता है; अंतिम नोड कभी नहीं हटाया जाता। हर परिवर्तन इवेंट लॉग में दर्ज होता है, अलर्ट नियम "DNS failover: node taken out" ऑप्स अलर्ट के माध्यम से सूचित करता है। API टोकन सर्वर कीरिंग से एन्क्रिप्ट किया जाता है और फिर कभी नहीं दिखाया जाता।
# Admin: Domains > DNS failover (cluster). Stored cluster-wide; the token is encrypted
# with the server keyring and never shown again. Equivalent settings:
Provider: Hetzner DNS (Cloud API, api.hetzner.cloud) | Cloudflare DNS
Zone: example.com
Names: mail.example.com # A/AAAA follow the healthy nodes
TTL: 60 # resolvers keep old answers this long
Interval: 30 s
Failures (N): 3 # 3 x 30 s before a node leaves DNS
Successes (M): 5 # 5 x 30 s before it returns
Checks: SMTP 25 (220 banner), HTTPS /health
Nodes: fsn1, 192.0.2.10, 2001:db8::10
nbg1, 198.51.100.20
# Alert: rule "DNS failover: node taken out" (Alerts) fires when a node is out of DNS.
Target Session Attributes=primary होने पर (ऊपर अध्याय 04) कटा हुआ पक्ष किसी प्राइमरी तक नहीं पहुँचता, लॉक प्राप्त नहीं करता और निष्क्रिय रहता है। सभी पर्यवेक्षकों पर बहुमत मतदान किसी गलत दृष्टिकोण वाले एकल नोड को किसी स्वस्थ नोड को हटाने से रोकता है।विकल्प: etcd लॉक के अंतर्गत स्क्रिप्ट
अंतर्निहित कनेक्टर के बिना DNS प्रदाताओं (उदा. deSEC) के लिए, या जब निर्णय etcd को लेना हो, तो स्क्रिप्ट एक समकक्ष विकल्प बनी रहती है। एक ही नामों के लिए एजेंट और स्क्रिप्ट को एक साथ न चलाएँ। स्क्रिप्ट हर 20 s पर हर नोड के /health/ready को उसके सार्वजनिक IP के माध्यम से जाँचती है और Cloudflare API (dns_records पर PATCH/POST/DELETE) के माध्यम से A रिकॉर्ड का मिलान करती है। यह हर साइट पर etcdctl lock के तहत चलती है: केवल एक प्रति कार्य करती है, और केवल उस पक्ष पर जहाँ etcd बहुमत है। इसलिए कटी हुई साइट अब DNS नहीं बदल सकती। Hetzner DNS या deSEC के लिए केवल तीन curl कॉल बदलते हैं। [39]
#!/usr/bin/env bash
# /usr/local/bin/nextxch-dns-failover.sh
# ALTERNATIVE to the built-in agent (admin: Domains > DNS failover), e.g. for DNS
# providers without a built-in connector (deSEC) or when etcd decides anyway.
# Keeps the A records of mail.example.com equal to the set of healthy nodes.
# Runs on every site under an etcd lock, so only one copy acts, and only on the
# side that still has etcd quorum (a cut-off minority site can never change DNS).
# ExecStart=etcdctl --endpoints=http://127.0.0.1:2379 lock nextxch-dns /usr/local/bin/nextxch-dns-failover.sh
# Needs: curl, jq. Secrets via systemd EnvironmentFile (CF_API_TOKEN, CF_ZONE_ID).
set -euo pipefail
NAME="mail.example.com"
TTL=60
NODES=("192.0.2.10" "198.51.100.20") # public IPs of the nextxch nodes
FAILS_BEFORE_REMOVE=3 # 3 x 20 s = 1 minute
STATE=/run/nextxch-failover; mkdir -p "$STATE"
API="https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records"
AUTH=(-H "Authorization: Bearer ${CF_API_TOKEN}" -H "Content-Type: application/json")
healthy() { # HTTPS readiness through the public IP, with SNI for the real name
curl -fsS --max-time 5 --resolve "${NAME}:443:$1" "https://${NAME}/health/ready" \
| jq -e '.status == "ready"' >/dev/null
}
while true; do
want=()
for ip in "${NODES[@]}"; do
f="$STATE/$ip"; n=$(cat "$f" 2>/dev/null || echo 0)
if healthy "$ip"; then echo 0 > "$f"; want+=("$ip")
else n=$((n+1)); echo "$n" > "$f"; [ "$n" -lt "$FAILS_BEFORE_REMOVE" ] && want+=("$ip"); fi
done
# Never publish an empty set: if everything looks down, the checker is probably the problem.
[ "${#want[@]}" -eq 0 ] && { sleep 20; continue; }
have=$(curl -fsS "${AUTH[@]}" "${API}?type=A&name=${NAME}" | jq -r '.result[] | "\(.id) \(.content)"')
for ip in "${want[@]}"; do # add missing healthy IPs
grep -q " ${ip}$" <<<"$have" || curl -fsS "${AUTH[@]}" -X POST "$API" \
--data "{\"type\":\"A\",\"name\":\"${NAME}\",\"content\":\"${ip}\",\"ttl\":${TTL},\"proxied\":false}" >/dev/null
done
while read -r id ip; do # remove IPs that are no longer healthy
[ -z "${id:-}" ] && continue
printf '%s\n' "${want[@]}" | grep -qx "$ip" || curl -fsS "${AUTH[@]}" -X DELETE "${API}/${id}" >/dev/null
done <<<"$have"
sleep 20
done
# /etc/systemd/system/nextxch-dns-failover.service (install on every site)
[Unit]
Description=nextxch DNS failover (runs only while holding the etcd lock)
After=network-online.target etcd.service wg-quick@wg0.service
Wants=network-online.target
[Service]
EnvironmentFile=/etc/nextxch/dns-failover.env
ExecStart=/usr/bin/etcdctl --endpoints=http://127.0.0.1:2379 lock nextxch-dns /usr/local/bin/nextxch-dns-failover.sh
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
05 · सेटअप M: तीन पूर्ण साइटें (Proxmox या सामान्य VM)
सेटअप S की तरह, लेकिन तीनों साइटें PostgreSQL रेप्लिका के साथ nextxch चलाती हैं। जब एक साइट विफल होती है, तो दो डेटाबेस और दो nextxch नोड बचे रहते हैं, और synchronous_mode के साथ सिंक्रोनस रेप्लिकेशन भी बना रहता है। समान प्राथमिकता वाले तीन MX रिकॉर्ड इनबाउंड लोड को फैलाते हैं।
flowchart TB
S["Remote mail servers"] -->|"MX 10 mx1, mx2, mx3"| N
U["Users"] -->|"mail.example.com"| N
subgraph N["WireGuard mesh 10.88.0.0/24"]
direction LR
subgraph A["Hetzner"]
A1["nextxch"] --- A2[("Patroni + PG")] --- A3["etcd + Garage"]
end
subgraph B["netcup"]
B1["nextxch"] --- B2[("Patroni + PG")] --- B3["etcd + Garage"]
end
subgraph C["Third provider"]
C1["nextxch"] --- C2[("Patroni + PG")] --- C3["etcd + Garage"]
end
end
N --> BK["Backups: Storage Box + S3 at another provider"]
चरण दर चरण
- तीन प्रदाताओं पर तीन VM (या Proxmox गेस्ट), एक ही Debian संस्करण, प्रत्येक का PTR mx1/mx2/mx3 पर।
- तीन होस्ट वाली Ansible इन्वेंटरी, सभी के लिए एक प्लेबुक (WireGuard, etcd, Patroni, Garage, nextxch)।
- सेटअप S का Patroni कॉन्फ़िगरेशन, एक तीसरे होस्ट के साथ; synchronous_mode: on, तीन डेटाबेस के साथ वैकल्पिक रूप से quorum। [54]
- तीन होस्ट वाली कनेक्शन स्ट्रिंग, तीन ज़ोन वाला Garage लेआउट, तीन MX रिकॉर्ड, SPF और DNS फेलओवर नोड सूची में तीन IP।
- VM इमेज के लिए Proxmox Backup Server वैकल्पिक: डेटास्टोर स्थानीय या S3 (PBS 4.2 से S3 बैकएंड आधिकारिक रूप से समर्थित); Proxmox, CIFS के माध्यम से Storage Box जैसे नेटवर्क शेयर को डेटास्टोर प्रकार के रूप में प्रलेखित नहीं करता। [46]
# inventory/ha.yml: three sites, one VM (or Proxmox guest) per site
all:
vars:
ansible_user: deploy
wg_network: 10.88.0.0/24
pg_major: 18
children:
nextxch_sites:
hosts:
hetzner-a: { ansible_host: 192.0.2.10, wg_ip: 10.88.0.1, garage_zone: hetzner }
netcup-b: { ansible_host: 198.51.100.20, wg_ip: 10.88.0.2, garage_zone: netcup }
third-c: { ansible_host: 203.0.113.30, wg_ip: 10.88.0.3, garage_zone: third }
# site.yml: idempotent base for every site (same playbook for Hetzner, netcup, Strato ...)
- hosts: nextxch_sites
become: true
tasks:
- name: Base packages (Debian 13)
ansible.builtin.apt:
name: [wireguard, etcd-server, etcd-client, patroni, pgbackrest, docker.io, docker-compose-v2, jq, curl]
update_cache: true
# PostgreSQL 18 itself comes from the PGDG apt repository (apt.postgresql.org),
# Garage as the static binary or the dxflrs/garage image.
- name: WireGuard config
ansible.builtin.template: { src: wg0.conf.j2, dest: /etc/wireguard/wg0.conf, mode: "0600" }
notify: restart wg
- name: etcd, Patroni and Garage config
ansible.builtin.template: { src: "{{ item.src }}", dest: "{{ item.dest }}", mode: "0640" }
loop:
- { src: etcd.env.j2, dest: /etc/default/etcd }
- { src: patroni.yml.j2, dest: /etc/patroni/config.yml }
- { src: garage.toml.j2, dest: /etc/garage.toml }
- name: nextxch compose file
ansible.builtin.template: { src: docker-compose.yml.j2, dest: /opt/nextxch/docker-compose.yml }
- name: Start nextxch
ansible.builtin.command: docker compose up -d
args: { chdir: /opt/nextxch }
changed_when: false
handlers:
- name: restart wg
ansible.builtin.service: { name: wg-quick@wg0, state: restarted }
06 · सेटअप L: तीन प्रदाताओं पर Kubernetes
बहुत सारे मेलबॉक्स और क्षैतिज स्केलिंग के लिए। पहले एक महत्वपूर्ण बात: k3s कई क्लाउड में फैले क्लस्टर में एम्बेडेड etcd को स्पष्ट रूप से समर्थित नहीं करता; सर्वर नोड एक ही स्थान पर रहने चाहिए। इसलिए: प्रति प्रदाता एक Kubernetes क्लस्टर (प्रत्येक k3s या Talos), जिनमें से प्रत्येक साथ दिए गए Helm चार्ट से एक nextxch डिप्लॉयमेंट चलाता है। डेटा लेयर (Patroni + etcd + Garage) सेटअप M की तरह WireGuard पर अपने अलग VM पर चलती है और तीनों क्लस्टर इसका उपयोग करते हैं। [49][48]
flowchart TB
DNS["DNS: MX per provider LB, mail.example.com with health checks"]
subgraph K1["k3s/Talos cluster Hetzner"]
L1["Load balancer"] --> P1["nextxch pods (HPA)"]
end
subgraph K2["k3s/Talos cluster netcup"]
L2["Load balancer"] --> P2["nextxch pods (HPA)"]
end
subgraph K3["k3s/Talos cluster STRATO"]
L3["Load balancer"] --> P3["nextxch pods (HPA)"]
end
DNS --> L1
DNS --> L2
DNS --> L3
subgraph D["Data layer on VMs, WireGuard"]
PG[("Patroni: primary + 2 replicas")]
GA["Garage: 3 zones"]
ET["etcd: 3 members"]
end
P1 --> PG
P2 --> PG
P3 --> PG
P1 --> GA
P2 --> GA
P3 --> GA
क्लस्टरों के बीच CloudNativePG क्यों नहीं?
CloudNativePG अन्य Kubernetes क्लस्टर में रेप्लिका क्लस्टर चला सकता है, लेकिन क्लस्टरों के बीच प्रमोशन और डिमोशन घोषणात्मक हैं (फ़ील्ड primary, promotionToken), यानी स्वचालित नहीं। स्वचालित प्रदाता फेलओवर के लिए तीन साइटों पर etcd के साथ Patroni अधिक सीधा समाधान है। [52]
चरण दर चरण
- डेटा लेयर को सेटअप M की तरह बनाएँ (Patroni, etcd, Garage के साथ तीन VM)।
- प्रति प्रदाता एक k3s या Talos क्लस्टर; WireGuard (या KubeSpan) के माध्यम से डेटा लेयर से नेटवर्क लिंक।
- इनबाउंड पोर्ट:
externalTrafficPolicy: Localके साथ LoadBalancer प्रकार की सेवा (रिपॉज़िटरी की तरह), ताकि SPF/DMARC के लिए प्रेषक IP सुरक्षित रहे; PROXY प्रोटोकॉल समर्थित लोड बैलेंसर के साथ इसके बजायCluster__EnableProxyProtocol=trueका उपयोग करें। Hetzner Cloud Load Balancer PROXY प्रोटोकॉल और किसी भी TCP सेवा का समर्थन करता है। [19] - आउटबाउंड पोर्ट 25: मेल खाने वाले PTR और SPF प्रविष्टि वाले एक निश्चित IP के माध्यम से पॉड egress (egress गेटवे, जैसा कि deploy/kubernetes/README.md में वर्णित है)।
- प्रति प्रदाता Helm values, पाइपलाइन के माध्यम से एक के बाद एक रोलआउट (अध्याय 07)। HPA (70% CPU पर 2 से 10 पॉड) और PodDisruptionBudget रिपॉज़िटरी के साथ आते हैं।
# values-hetzner.yaml (one file per provider cluster; netcup/strato analogue)
# Helm chart: deploy/helm/nextxch in the nextxch repository.
replicaCount: 3
mail:
hostname: mail.example.com
defaultDomain: example.com
postgres:
# Patroni data layer outside Kubernetes, reached through WireGuard / KubeSpan
connectionString: "Host=10.88.0.1,10.88.0.2,10.88.0.3;Database=nextxch;Username=nextxch;Password=<from secret>;Target Session Attributes=primary"
s3:
endpointUrl: "http://garage.nextxch-data.svc:3900" # Garage zone of this provider
bucketName: "nextxch"
07 · Git से डिप्लॉय और अपडेट
हर महत्वपूर्ण चीज़ रिपॉज़िटरी में एक फ़ाइल है: compose फ़ाइल या Helm values, Ansible इन्वेंटरी, DNS फेलओवर स्क्रिप्ट। पाइपलाइन हमेशा केवल एक साइट पर रोल आउट करती है और आगे बढ़ने से पहले तब तक प्रतीक्षा करती है जब तक /health/ready फिर से "ready" न बताए। सीक्रेट (लाइसेंस, पासवर्ड, API टोकन, SSH कुंजियाँ) केवल GitHub/GitLab सीक्रेट स्टोर में या सर्वर पर .env में रहते हैं, कभी भी रिपॉज़िटरी में नहीं।
flowchart TB
G["git push to main"] --> V["Validate: compose config / helm lint"]
V --> A["Roll out site A"]
A --> HA{"/health/ready = ready?"}
HA -->|"yes"| B["Roll out site B"]
HA -->|"no"| X["Abort, B stays on old version"]
B --> HB{"/health/ready = ready?"}
HB -->|"yes"| C["Roll out site C"]
HB -->|"no"| X
C --> OK["Done"]
# .github/workflows/deploy.yml: rolling update of the compose sites, one site at a time.
name: deploy-nextxch
on:
push:
branches: [main]
paths: ["sites/**", "compose/**"]
workflow_dispatch:
concurrency: { group: deploy-nextxch, cancel-in-progress: false }
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
max-parallel: 1 # never update two sites at once
matrix:
site: [hetzner-a, netcup-b]
environment: ${{ matrix.site }}
steps:
- uses: actions/checkout@v4
- name: Write SSH key
run: |
install -m 700 -d ~/.ssh
echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519
echo "${{ vars.HOST_KEY }}" >> ~/.ssh/known_hosts
- name: Upload compose file and roll the container
env:
HOST: ${{ vars.SSH_HOST }}
run: |
scp compose/docker-compose.yml "deploy@${HOST}:/opt/nextxch/docker-compose.yml"
scp "sites/${{ matrix.site }}.env.tag" "deploy@${HOST}:/opt/nextxch/tag.env"
ssh "deploy@${HOST}" 'cd /opt/nextxch && docker compose --env-file .env --env-file tag.env pull && docker compose --env-file .env --env-file tag.env up -d'
- name: Wait until the node is ready again
env:
HOST: ${{ vars.SSH_HOST }}
run: |
for i in $(seq 1 30); do
ssh "deploy@${HOST}" 'curl -fsS http://127.0.0.1/health/ready' && exit 0
sleep 10
done
echo "node did not become ready" >&2; exit 1
# .gitlab-ci.yml: the same rolling update with GitLab CI
stages: [validate, deploy-a, deploy-b]
variables:
NEXTXCH_TAG: "latest" # pin a released version in production
validate:
stage: validate
image: docker:27
script:
- docker compose -f compose/docker-compose.yml config -q
.deploy: &deploy
image: alpine:3.22
before_script:
- apk add --no-cache openssh-client curl
- install -m 700 -d ~/.ssh && echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519
- echo "$HOST_KEY" >> ~/.ssh/known_hosts
script:
- scp compose/docker-compose.yml "deploy@$SSH_HOST:/opt/nextxch/"
- ssh "deploy@$SSH_HOST" "cd /opt/nextxch && NEXTXCH_TAG=$NEXTXCH_TAG docker compose pull && NEXTXCH_TAG=$NEXTXCH_TAG docker compose up -d"
- for i in $(seq 1 30); do ssh "deploy@$SSH_HOST" 'curl -fsS http://127.0.0.1/health/ready' && exit 0; sleep 10; done; exit 1
resource_group: production # serializes deployments
rules: [ { if: '$CI_COMMIT_BRANCH == "main"' } ]
deploy-hetzner-a:
<<: *deploy
stage: deploy-a
environment: { name: hetzner-a }
deploy-netcup-b:
<<: *deploy
stage: deploy-b
environment: { name: netcup-b }
# .github/workflows/deploy-k8s.yml: one Kubernetes cluster per provider, rolled one after another.
name: deploy-nextxch-k8s
on:
push: { branches: [main], paths: ["k8s/**"] }
workflow_dispatch:
concurrency: { group: deploy-k8s, cancel-in-progress: false }
jobs:
helm:
runs-on: ubuntu-latest
strategy:
max-parallel: 1
matrix:
cluster: [hetzner, netcup, strato]
environment: k8s-${{ matrix.cluster }}
steps:
- uses: actions/checkout@v4
- uses: azure/setup-helm@v4
- name: Kubeconfig
run: |
mkdir -p ~/.kube
echo "${{ secrets.KUBECONFIG_B64 }}" | base64 -d > ~/.kube/config
chmod 600 ~/.kube/config
- name: Helm upgrade (waits for readiness probes on /health/ready)
run: |
helm upgrade --install nextxch ./deploy/helm/nextxch \
--namespace nextxch --create-namespace \
-f k8s/values-common.yaml -f k8s/values-${{ matrix.cluster }}.yaml \
--set image.tag=${{ vars.NEXTXCH_TAG }} \
--wait --timeout 10m --atomic
nextxch-cli config plan पुल रिक्वेस्ट पर प्लान के साथ टिप्पणी करता है, config apply --prune --yes मर्ज के बाद उसे लागू करता है। तैयार GitHub Actions और GitLab CI फ़ाइलों के साथ गाइड: कॉन्फ़िगरेशन ऐज़ कोड (GitOps)।08 · विफलता परिदृश्य: ठीक-ठीक क्या होता है
Hetzner (साइट A, वर्तमान प्राइमरी) पूरी तरह ऑफ़लाइन हो जाता है
sequenceDiagram participant S as Remote mail server participant A as Site A (Hetzner) participant B as Site B (netcup) participant C as Witness C participant D as DNS Note over A: Provider outage S->>A: SMTP to mx1 A--xS: no connection S->>B: SMTP to mx2 (or retry) B--xS: without a primary the session fails, sender retries later Note over B,C: etcd majority B + C, leader key of A expires (ttl 30 s) B->>B: Patroni promotes PostgreSQL B to primary B->>B: nextxch reconnects (Target Session Attributes=primary) S->>B: retry B-->>S: 250 OK (commit synchronous or asynchronous) C->>D: failover job removes IP A from mail.example.com Note over D: clients follow after the TTL expires
| समय | क्या होता है | उपयोगकर्ता क्या महसूस करते हैं |
|---|---|---|
| 0 s | A उपलब्ध नहीं है। mx1 पर इनबाउंड मेल विफल होता है, प्रेषक mx2 (समान प्राथमिकता) का उपयोग करते हैं या बाद में पुनः प्रयास करते हैं। [1] | A पर क्लाइंट का कनेक्शन टूट जाता है। |
| लगभग 30 s तक | etcd में A की लीडर कुंजी ttl = 30 s के बाद समाप्त हो जाती है; B और C के पास बहुमत है। [53] | नोड B /health/ready पर 503 लौटाता है, लेखन प्रतीक्षा करते हैं। |
| लगभग 30 से 60 s | Patroni, B को प्रमोट करता है (loop_wait 10 s)। B पर nextxch मल्टी-होस्ट कनेक्शन स्ट्रिंग के माध्यम से नया प्राइमरी ढूँढता है (recheck 5 से 10 s)। [63] | B फिर से मेल स्वीकार करता है। |
| लगभग 60 से 90 s | DNS फेलओवर (एजेंट: 3 विफल राउंड × 30 s, स्क्रिप्ट: 3 × 20 s) mail.example.com से IP A हटा देता है। | TTL (60 s) समाप्त होते ही नए कनेक्शन B पर जाते हैं। |
| 1 से 5 मिनट | अपने स्वयं के न्यूनतम TTL वाले रिज़ॉल्वर और DNS कैश वाले क्लाइंट बाद में अनुसरण करते हैं। Garage ज़ोन B और C के साथ चलता रहता है (3 में से 2 का कोरम)। [61] | क्लाइंट फिर से कनेक्ट होते हैं; वेबमेल सत्र मान्य रहते हैं (हस्ताक्षरित, स्टेटलेस सत्र)। |
RPO
synchronous_mode के साथ, PostgreSQL किसी commit की पुष्टि तभी करता है जब वह दूसरी साइट पर पहुँच चुका हो; 250 OK से पुष्टि किया गया संदेश तब दो साइटों पर मौजूद होता है। कीमत: हर commit साइटों के बीच कम से कम एक राउंड ट्रिप की प्रतीक्षा करता है। synchronous_mode के बिना आउटेज से पहले के अंतिम सेकंड गायब हो सकते हैं। synchronous_mode_strict: false के साथ, कोई रेप्लिका न बचने पर Patroni एसिंक्रोनस पर लौट आता है, ताकि मेल स्वीकार होता रहे। [56][54]
RTO
डेटाबेस लगभग 30 से 60 s (Patroni डिफ़ॉल्ट), DNS स्विच लगभग 1 मिनट और TTL, इनबाउंड मेल दूसरे MX या प्रेषक के पुनः प्रयास के माध्यम से बिना प्रतीक्षा के। ये समय डिफ़ॉल्ट से निकाले गए हैं, मापे नहीं गए: अपने स्वयं के परीक्षण में सत्यापित करें (किसी साइट को हार्ड-स्टॉप करें)।
netcup (साइट B, रेप्लिका) ऑफ़लाइन हो जाता है
A पर प्राइमरी चलता रहता है। सिंक्रोनस पार्टनर के बिना Patroni एसिंक्रोनस पर स्विच करता है (strict = false), फेलओवर जॉब IP B हटा देता है, इनबाउंड मेल mx1 से प्रवाहित होता है। Garage A और C के साथ जारी रहता है।
केवल A और B के बीच का लिंक बंद है
जो पक्ष अब भी विटनेस C तक पहुँचता है, वह etcd बहुमत बनाए रखता है और प्राइमरी बना रहता है या बन जाता है। दूसरा पक्ष अपना बहुमत खो देता है, Patroni वहाँ प्राइमरी को डिमोट कर देता है (failsafe_mode केवल तभी लागू होता है जब वह अब भी सभी सदस्यों तक पहुँच सके), और DNS फेलओवर वहाँ कार्रवाई नहीं करता: बिल्ट-इन एजेंट किसी प्राइमरी तक नहीं पहुँचता और उसे लीडर लॉक नहीं मिलता, स्क्रिप्ट को etcd लॉक नहीं मिलता। कोई स्प्लिट ब्रेन नहीं होता। [55]
फेलबैक
sequenceDiagram participant A as Site A participant B as Site B (primary) participant G as Garage participant D as DNS job A->>A: server back online, WireGuard up A->>B: Patroni: pg_rewind, then replica of B G->>A: missing objects are re-replicated A->>A: /health/ready = ready D->>D: successful checks D->>A: IP A back in mail.example.com Note over A,B: optional, planned: patronictl switchover back to A
जब A वापस आता है, तो B प्राइमरी बना रहता है। Patroni, pg_rewind (use_pg_rewind: true) के साथ A को रेप्लिका के रूप में वापस लाता है, Garage लुप्त ऑब्जेक्ट को फिर से सिंक करता है, और जैसे ही वहाँ /health/ready "ready" रिपोर्ट करता है, फेलओवर जॉब IP A को फिर से जोड़ देता है। प्राइमरी को वापस ले जाना आवश्यक नहीं है; यदि चाहें, तो इसे शांत समय पर patronictl switchover से योजनाबद्ध तरीके से करें।
09 · बैकअप, पुनर्स्थापना और GoBD
प्रतिकृति आउटेज से बचाती है, गलतियों से नहीं: गलती से हटाया गया मेलबॉक्स एक साथ सभी साइटों से चला जाता है। इसलिए 3-2-1 नियम के अनुसार बैकअप जोड़ें: तीन प्रतियाँ, दो मीडिया प्रकार, एक ऑफ़साइट। [59]
flowchart LR
PG[("PostgreSQL primary or replica")] -->|"pgBackRest, continuous WAL"| R1["Repo 1: Hetzner Storage Box (SFTP)"]
PG -->|"pgBackRest"| R2["Repo 2: S3 at another provider"]
GA["Garage (3 zones)"] -->|"rclone copy hourly"| AR["Object Storage with Object Lock (GoBD)"]
GA -->|"rclone copy"| R1
R1 --> T["Quarterly restore test"]
R2 --> T
- PostgreSQL: सतत WAL आर्काइविंग के साथ pgBackRest (point-in-time recovery)। रिपॉज़िटरी 1 SFTP के माध्यम से Hetzner Storage Box पर (pgBackRest 2.46 से SFTP का समर्थन करता है; Storage Box SFTP, SCP, rsync, BorgBackup, Restic, Rclone, SMB, WebDAV प्रदान करता है), रिपॉज़िटरी 2 किसी अन्य प्रदाता के S3 पर। दोनों एन्क्रिप्टेड। [58][15]
- ब्लॉब: .eml ऑब्जेक्ट का नाम SHA-256 से रखा जाता है और उन्हें कभी संशोधित नहीं किया जाता। प्रति घंटे की कॉपी के रूप में
rclone copy(sync नहीं, ताकि यह कभी कुछ न हटाए) पर्याप्त है। - GoBD आर्काइव: Hetzner Object Storage, GOVERNANCE और COMPLIANCE मोड में Object Lock तथा वर्ज़निंग का समर्थन करता है; Object Lock बकेट बनाते समय ही सक्षम करना होगा। बकेट को डिफ़ॉल्ट प्रतिधारण (compliance) के साथ बनाएँ, फिर हर कॉपी किया गया ऑब्जेक्ट स्वचालित रूप से लॉक हो जाता है। वैकल्पिक रूप से nextxch को सीधे इस बकेट में लिखने दें (S3 या Hybrid बैकएंड), लेकिन तब ब्लॉब की उपलब्धता Hetzner पर निर्भर करती है। उस स्थिति में WORM मोड कॉन्फ़िगर होते ही (या
Storage:S3:ObjectLockEnabled) nextxch स्टार्टअप पर अनुपलब्ध बकेट को Object Lock के साथ बनाता है; GoBD आर्काइव में "S3 बकेट जाँचें" पहुँच, वर्ज़निंग, Object Lock मोड और डिफ़ॉल्ट प्रतिधारण दिखाता है। [18][17] - रिस्टोर का परीक्षण करें: कम से कम हर तिमाही pgBackRest को एक टेस्ट VM में रिस्टोर करें और blob प्रति से नमूने खोलें।
# /etc/pgbackrest/pgbackrest.conf on every PostgreSQL site.
# Repo 1: Hetzner Storage Box over SFTP (pgBackRest 2.46+), repo 2: S3 at another provider.
[global]
repo1-type=sftp
repo1-path=/pgbackrest
repo1-sftp-host=uXXXXXX.your-storagebox.de
repo1-sftp-host-port=23
repo1-sftp-host-user=uXXXXXX
repo1-sftp-private-key-file=/var/lib/postgresql/.ssh/id_ed25519
repo1-sftp-host-key-hash-type=sha256
repo1-retention-full=2
repo1-cipher-type=aes-256-cbc
repo1-cipher-pass=<long random secret, stored outside the repo>
repo2-type=s3
repo2-path=/pgbackrest
repo2-s3-endpoint=<s3 endpoint of the second provider>
repo2-s3-bucket=nextxch-pg-backup
repo2-s3-region=<region>
repo2-s3-uri-style=path
repo2-retention-full=4
repo2-cipher-type=aes-256-cbc
repo2-cipher-pass=<second secret>
archive-async=y
process-max=2
start-fast=y
[nextxch]
pg1-path=/var/lib/postgresql/18/main
pg1-port=5432
# Weekly full, daily differential (cron on every node; pgBackRest runs only where it can,
# use --repo and the "backup-standby" option to take backups from a replica).
# 0 2 * * 0 pgbackrest --stanza=nextxch --type=full backup
# 0 2 * * 1-6 pgbackrest --stanza=nextxch --type=diff backup
# Blob copy (all .eml objects) from Garage to an independent target, every hour.
# ~/.config/rclone/rclone.conf (keys are placeholders)
[garage]
type = s3
provider = Other
endpoint = http://127.0.0.1:3900
region = garage
access_key_id = <garage key>
secret_access_key = <garage secret>
[archive]
type = s3
provider = Other
endpoint = <object storage endpoint>
access_key_id = <key>
secret_access_key = <secret>
# Content-addressed (SHA-256 names, never modified): "copy" is enough and never deletes.
# rclone copy garage:nextxch archive:nextxch-archive --immutable --transfers 8 --checksum
# For a GoBD copy create the target bucket WITH Object Lock and a default retention
# (COMPLIANCE, e.g. 10 years) so every copied object is locked automatically.
10 · स्केलिंग: उपयोगकर्ता, स्टोरेज, पॉड
flowchart LR S1["Start: 2 sites + witness"] --> S2["More mailboxes: add nextxch nodes per site"] S2 --> S3["DB grows: scale PostgreSQL VMs vertically (RAM, NVMe)"] S3 --> S4["More storage: add Garage nodes per zone"] S4 --> S5["Many mailboxes: Kubernetes per provider, HPA"]
| घटक | स्केलिंग | नोट |
|---|---|---|
| nextxch नोड / पॉड | क्षैतिज रूप से | स्टेटलेस; प्रति साइट अधिक नोड या अधिक पॉड (HPA)। स्टिकी सत्र आवश्यक नहीं। |
| PostgreSQL प्राइमरी | वर्टिकल रूप से | सभी लेखन (writes) एक ही प्राइमरी पर जाते हैं। अधिक नोड के बजाय अधिक RAM और तेज़ डिस्क (IOPS); shared_buffers का प्रारंभिक मान RAM का 25%। nextxch रीड रेप्लिका का उपयोग नहीं करता। [57] |
| ब्लॉब स्टोर | क्षैतिज रूप से | प्रति ज़ोन क्षमता के साथ अधिक Garage नोड (garage layout assign -c), डेटा पुनः संतुलित होता है। SHA-256 डीडुप्लिकेशन और Zstd संपीड़न इसकी आवश्यकता कम करते हैं। |
| SMTP | DNS के माध्यम से | समान प्राथमिकता वाले अधिक MX रिकॉर्ड लोड को फैलाते हैं; आउटबाउंड कतार SKIP LOCKED के माध्यम से सभी नोड पर फैलती है। |
| IMAP | DNS / LB के माध्यम से | लंबे समय तक चलने वाले IDLE कनेक्शन अपने नोड पर रहते हैं; अन्य नोड से नई मेल LISTEN/NOTIFY के माध्यम से आती है। नोड जोड़ने के बाद, लोड केवल तभी फैलता है जब क्लाइंट दोबारा कनेक्ट होते हैं। [7] |
| HTTPS (वेबमेल, ActiveSync, JMAP) | DNS / LB के माध्यम से | स्टेटलेस HMAC सत्र; कोई भी अनुरोध किसी भी नोड पर पहुँच सकता है। |
11 · लागत: कहाँ बचत करें, कहाँ नहीं
सहेजें
- nextxch और विटनेस के लिए साझा vCPU प्लान; समर्पित कोर केवल तब जब डेटाबेस CPU लोड इसकी आवश्यकता दिखाए।
- netcup पर 12 महीने की अवधि (मूल्य पेज के अनुसार मासिक मूल्य से 13% कम)। [22]
- सशुल्क लोड बैलेंसर के बजाय बिल्ट-इन एजेंट (या अपनी स्क्रिप्ट) के साथ DNS फेलओवर।
- ब्लॉब के लिए Zstd कम्प्रेशन (Storage:Compression) और SHA-256 डिडुप्लीकेशन।
इसमें बचत न करें
- PostgreSQL नोड की RAM: जो कुछ भी कैश नहीं है, वह हर फ़ोल्डर दृश्य पर डिस्क IO खर्च करता है।
- PostgreSQL (NVMe) के लिए डिस्क IOPS; WAL राइट हर डिलीवरी की लेटेंसी तय करते हैं।
- तीसरी साइट: इसके बिना कोई सुरक्षित स्वचालित फेलओवर नहीं होता।
- एक स्वतंत्र लक्ष्य पर परीक्षित बैकअप।
संग्रहीत मूल्य (शुद्ध प्रति माह, 2 नवंबर 2026 की स्थिति)। netcup और STRATO 19% VAT सहित सकल मूल्य दिखाते हैं, जिन्हें यहाँ 1.19 से विभाजित किया गया है। Hetzner के लिए प्राथमिक IPv4 के €0.50 जोड़े गए हैं। जाँच की तिथि पर Hetzner ने सभी क्लाउड प्लान को "currently unavailable" दिखाया; यह स्टॉक के कारण था या प्रदर्शन के कारण, यह निर्धारित नहीं किया जा सका। Hetzner वॉल्यूम का मूल्य आधिकारिक पेजों से पढ़ा नहीं जा सका, इसलिए यह कैलकुलेटर में नहीं है। मूल्य बदलते रहते हैं: ऑर्डर करने से पहले प्रदाता से जाँच करें। [12][10][22][23][30][15][16]
| प्रदाता | योजना | vCPU | RAM | डिस्क | € शुद्ध / माह |
|---|---|---|---|---|---|
| Hetzner Cloud | CX23 | 2 | 4 GB | 40 GB | €6.49 |
| Hetzner Cloud | CX33 | 4 | 8 GB | 80 GB | €9.49 |
| Hetzner Cloud | CX43 | 8 | 16 GB | 160 GB | €16.99 |
| Hetzner Cloud | CX53 | 16 | 32 GB | 320 GB | €30.49 |
| netcup VPS | VPS 500 G12.5 | 2 | 4 GB | 64 GB | €6.94 |
| netcup VPS | VPS 1000 G12.5 | 4 | 8 GB | 128 GB | €12.18 |
| netcup VPS | VPS 2000 G12.5 | 8 | 16 GB | 256 GB | €22.62 |
| netcup VPS | VPS 4000 G12.5 | 12 | 32 GB | 512 GB | €38.12 |
| netcup VPS | VPS 8000 G12.5 | 16 | 64 GB | 1024 GB | €56.39 |
| netcup Root Server | RS 1000 G12.5 | 4 | 8 GB | 128 GB | €18.26 |
| netcup Root Server | RS 2000 G12.5 | 8 | 16 GB | 256 GB | €34.20 |
| netcup Root Server | RS 4000 G12.5 | 12 | 32 GB | 512 GB | €65.40 |
| netcup Root Server | RS 8000 G12.5 | 16 | 64 GB | 1024 GB | €124.72 |
| netcup Root Server | RS 12000 G12.5 | 20 | 96 GB | 1536 GB | €182.26 |
| netcup Root Server | RS 16000 G12.5 | 24 | 128 GB | 2048 GB | €243.24 |
| STRATO VPS | VPS S | 1 | 2 GB | 60 GB | €3.36 |
| STRATO VPS | VPS M | 2 | 4 GB | 120 GB | €8.40 |
| STRATO VPS | VPS L | 4 | 8 GB | 240 GB | €16.81 |
| STRATO VPS | VPS XL | 8 | 16 GB | 480 GB | €32.77 |
| STRATO VPS | VPS XXL | 12 | 24 GB | 720 GB | €47.06 |
| Hetzner Storage Box | BX11 | - | - | 1 TB | €3.20 |
| Hetzner Storage Box | BX21 | - | - | 5 TB | €10.90 |
| Hetzner Storage Box | BX31 | - | - | 10 TB | €20.80 |
| Hetzner Storage Box | BX41 | - | - | 20 TB | €40.60 |
| Hetzner Object Storage | मूल मूल्य, 1 TB स्टोरेज + 1 TB egress सहित | - | - | 1 TB | €6.49 |
12 · कैलकुलेटर: संसाधन और लागत
पूरी तरह ब्राउज़र में चलता है, बाहरी सेवाओं के बिना। धारणाएँ परिणाम के नीचे खुले रूप से सूचीबद्ध हैं।
धारणाएँ (मापी नहीं गईं, कृपया अपनी संख्याओं से जाँचें)
- मेल वॉल्यूम = मेलबॉक्स × औसत आकार × (1 + वृद्धि)^वर्ष। डीडुप्लिकेशन या संपीड़न के लिए कोई कटौती नहीं (रूढ़िवादी अनुमान)।
- PostgreSQL आकार = मेल वॉल्यूम का 3% (मेटाडेटा, फ़्लैग, फ़ुल-टेक्स्ट इंडेक्स), कम से कम 2 GB। वास्तविक मान
SELECT pg_database_size('nextxch')से मिलता है। - PostgreSQL के लिए RAM = DB आकार का 25%, कम से कम 2 GB, 2/4/8/16/32/64/96/128 GB तक ऊपर पूर्णांकित। PostgreSQL के लिए डिस्क = 2 × DB आकार (डेटा, WAL, अतिरिक्त स्थान)।
- nextxch प्रक्रिया: 1 GB + हर शुरू किए गए 1,000 मेलबॉक्स पर 1 GB (रिपॉज़िटरी में Kubernetes सीमा प्रति पॉड 1 GiB है)। etcd + Garage + OS: 1 GB। विटनेस: 2 GB RAM। ऑपरेटिंग सिस्टम: 20 GB डिस्क।
- Garage replication_factor 3: हर ज़ोन (साइट या विटनेस) पूरा मेल वॉल्यूम संग्रहीत करता है।
- सेटअप L: प्रति प्रदाता कम से कम 2 पॉड, प्रति 1,500 मेलबॉक्स 1 पॉड, 8 GB वाले वर्कर; डेटा VM पर PostgreSQL, etcd और Garage ज़ोन रहते हैं।
- बैकअप आवश्यकता = 3 × DB आकार (2 पूर्ण बैकअप + WAL) + मेल वॉल्यूम; लक्ष्य: सबसे छोटा उपयुक्त Hetzner Storage Box।
- लाइसेंस मूल्य इस वेबसाइट की वर्तमान मूल्य सूची से (स्तर मूल्य सभी मेलबॉक्स पर लागू होता है, वार्षिक मूल्य / 12)। Cloudflare USD में दिखाया गया है और यूरो योग में नहीं जोड़ा गया है।
13 · एक और प्रदाता जोड़ना (उदाहरण STRATO)
- ऑर्डर करने से पहले: क्या PTR सेट किया जा सकता है? STRATO: VPS के लिए रिवर्स DNS ग्राहक लॉगिन में डोमेन के अंतर्गत, पहले A/AAAA रिकॉर्ड मौजूद होना चाहिए, इसमें 24 h तक लगते हैं। पोर्ट 25: रूट सर्वर के लिए STRATO आउटबाउंड को अवरुद्ध बताता है, जिसे सपोर्ट के माध्यम से अनब्लॉक किया जाता है; VPS के लिए कोई नियम प्रलेखित नहीं है। STRATO कोई फेलओवर IP प्रदान नहीं करता। [31][32][30]
- नए होस्ट को उसके अपने WireGuard IP और Garage ज़ोन के साथ Ansible इन्वेंटरी में जोड़ें, सभी साइटों पर पीयर जोड़ें, प्लेबुक चलाएँ।
- etcd: सदस्य को
etcdctl member addसे जोड़ें और ETCD_INITIAL_CLUSTER_STATE=existing के साथ शुरू करें। चार डेटाबेस साइटों के साथ etcd सदस्यों की संख्या विषम रखें। [48] - Patroni रेप्लिका के रूप में शुरू होता है और स्वचालित रूप से रेप्लिकेट करता है। Garage: नए ज़ोन के साथ
garage layout assign,garage layout apply। - DNS: IP को SPF में जोड़ें, समान प्राथमिकता वाला एक नया MX, नोड को DNS फेलओवर सूची (एजेंट या स्क्रिप्ट) में, नए MX नाम के लिए TLSA रिकॉर्ड, नए नोड का EHLO नाम और PTR, प्रमाणपत्र को इस नाम से विस्तारित करें। MTA-STS नीति नए MX को स्वचालित रूप से शामिल कर लेती है (स्वचालित MX सूची); फिर DNS एडवाइज़र से नया _mta-sts TXT मान प्रकाशित करें।
- साइट को पाइपलाइन मैट्रिक्स में जोड़ें और नई साइट के आउटेज का एक बार परीक्षण करें।
14 · सीमाएँ और कमियाँ (ईमानदारी से)
यह गाइड लिखते समय कोड में मिला। चार कमियाँ अब तक दूर हो चुकी हैं: कई MX नामों के साथ MTA-STS, प्रति नोड EHLO नाम, स्वचालित रूप से बनाया गया Object Lock बकेट और बिल्ट-इन DNS फेलओवर एजेंट। बाकी के लिए गाइड वर्णित तरीके से वैकल्पिक उपाय करती है।
| कमी | प्रभाव | वैकल्पिक उपाय |
|---|---|---|
| DNS फेलओवर एजेंट (बंद) | अंतर्निहित, opt-in: हर नोड से हेल्थ चेक, केवल क्लस्टर लीडर Hetzner DNS या Cloudflare के माध्यम से A/AAAA बदलता है। | अन्य DNS प्रदाता (उदा. deSEC): etcd लॉक के अंतर्गत स्क्रिप्ट (अध्याय 04)। |
| कई MX के साथ MTA-STS (हल किया गया) | प्रति-डोमेन नीति सभी MX नामों को सूचीबद्ध करती है (DNS MX, सर्वर और नोड नाम, या वाइल्डकार्ड के साथ अपनी सूची); नीति id सूची के अनुसार बदलती है। | हर MX परिवर्तन के बाद DNS सलाहकार से _mta-sts TXT मान प्रकाशित करें। |
| प्रति नोड EHLO नाम (हल किया गया) | Smtp:HeloHostname या /admin/ में प्रति-नोड सेटिंग, प्रति नोड PTR जाँच के साथ; सर्वर होस्टनाम पूरे क्लस्टर के लिए रहता है। | - |
| केवल एक S3 लक्ष्य | अलग-अलग प्रदाताओं के दो S3 स्टोर में डुअल राइट नहीं; Object Lock सुविधाओं के लिए Object Lock वाला S3 आवश्यक है। | तीन साइटों पर Garage और साथ में Object Lock बकेट में rclone copy। |
| Object Lock बकेट (बंद) | WORM मोड कॉन्फ़िगर होने पर, सर्वर स्टार्टअप पर लुप्त bucket को Object Lock के साथ बनाता है। Object Lock के बिना मौजूदा bucket की चेतावनी और अलर्ट के रूप में सूचना दी जाती है, क्योंकि Object Lock केवल निर्माण के समय ही सक्षम किया जा सकता है। [18] | GoBD आर्काइव में "Check S3 bucket" से कभी भी जाँचें। |
| कोई बिल्ट-इन बैकअप नहीं | सर्वर में डेटाबेस और ब्लॉब के लिए कोई बैकअप सेवा नहीं है। | pgBackRest और rclone (अध्याय 09)। |
| कोई रीड रेप्लिका नहीं | सभी क्वेरी प्राइमरी पर जाती हैं; रीड रेप्लिका विकास योजना में केवल "अनुरोध पर" के रूप में सूचीबद्ध हैं। | PostgreSQL प्राइमरी को वर्टिकल रूप से स्केल करें। |
| कोई मापा गया साइज़िंग बेंचमार्क नहीं | कैलकुलेटर में RAM और DB आकार अनुमान हैं। | अपने स्वयं के मापों से ओवरराइड करें। |
FAQ
क्या nextxch कई प्रदाताओं पर active-active चल सकता है?
हाँ। Cluster:Enabled=true और PostgreSQL के साथ कितने भी nextxch नोड LISTEN/NOTIFY, UNLOGGED टेबल और advisory lock के माध्यम से स्थिति साझा करते हैं। सभी नोड को एक ही PostgreSQL प्राइमरी और एक ही S3 ब्लॉब स्टोर तक पहुँचना चाहिए; प्रदाताओं के पार यह WireGuard नेटवर्क के माध्यम से होता है।
क्या मुझे लोड बैलेंसर या anycast की आवश्यकता है?
नहीं। इनबाउंड मेल को केवल समान प्राथमिकता वाले कई MX रिकॉर्ड चाहिए (RFC 5321 खंड 5.1)। IMAP, सबमिशन और HTTPS को छोटे TTL वाला एक नाम चाहिए, जिसके A रिकॉर्ड एक हेल्थ-चेक जॉब द्वारा बनाए रखे जाते हैं। Anycast/BGP संभव है, लेकिन जटिल है।
मुझे न्यूनतम कितनी साइटों की आवश्यकता है?
तीन वोट: दो पूर्ण साइटें और एक छोटा witness (etcd, Garage ज़ोन, फेलओवर चेकर), या तीन पूर्ण साइटें। केवल दो साइटों के साथ कोई भी पक्ष सुरक्षित रूप से नहीं बता सकता कि दूसरा विफल हुआ है या केवल लिंक टूटा है (split brain)।
क्या किसी प्रदाता के ऑफ़लाइन होने पर मेल खो जाती है?
प्रेषक कम से कम 4 से 5 दिनों तक पुनः प्रयास करते रहते हैं (RFC 5321 खंड 4.5.4.1)। Patroni synchronous_mode और Garage replication_factor 3 के साथ, 250 OK से स्वीकार किया गया संदेश पहले से ही दूसरी साइट पर संग्रहीत होता है।
15 · स्रोत
सभी बाहरी तथ्यों की जाँच 2 नवंबर 2026 को निम्नलिखित आधिकारिक स्रोतों से की गई। जिन बिंदुओं का सत्यापन नहीं हो सका, उन्हें पाठ में ऐसा ही चिह्नित किया गया है।
- RFC 5321 (SMTP): 4.1.4 EHLO, 4.5.4.1 retry, 5.1 MX: https://www.rfc-editor.org/rfc/rfc5321.html
- RFC 7208 (SPF): 4.6.4 DNS लुकअप सीमा, 5.6 ip4/ip6: https://www.rfc-editor.org/rfc/rfc7208.html
- RFC 6376 (DKIM): 3.1 सिलेक्टर: https://www.rfc-editor.org/rfc/rfc6376.html
- RFC 8461 (MTA-STS): 3.2, 4.1 mx पैटर्न: https://www.rfc-editor.org/rfc/rfc8461.html
- RFC 7672 (SMTP के लिए DANE): 3.1 TLSA 3 1 1: https://www.rfc-editor.org/rfc/rfc7672.html
- RFC 8314 (implicit TLS 465/993/995): https://www.rfc-editor.org/rfc/rfc8314.html
- RFC 2177 (IMAP IDLE, हर 29 मिनट में पुनः जारी): https://www.rfc-editor.org/rfc/rfc2177.html
- Google: ईमेल प्रेषक दिशानिर्देश (फ़ॉरवर्ड और रिवर्स DNS): https://support.google.com/a/answer/81126
- Hetzner Docs: Floating IPs: https://docs.hetzner.com/cloud/floating-ips/overview
- Hetzner Docs: IPv4 मूल्य निर्धारण: https://docs.hetzner.com/general/others/ipv4-pricing
- Hetzner Docs: Robot फेलओवर IP: https://docs.hetzner.com/robot/dedicated-server/ip/failover
- Hetzner Cloud: लागत-अनुकूलित प्लान: https://www.hetzner.com/cloud/cost-optimized/
- Hetzner Docs: क्लाउड सर्वर FAQ (पोर्ट 25/465): https://docs.hetzner.com/cloud/servers/faq/
- Hetzner Docs: PTR रिकॉर्ड: https://docs.hetzner.com/networking/dns/record-types/ptr-record/
- Hetzner Storage Box (मूल्य, प्रोटोकॉल): https://www.hetzner.com/storage/storage-box/
- Hetzner Object Storage (मूल्य): https://www.hetzner.com/storage/object-storage/
- Hetzner Docs: Object Storage अवलोकन: https://docs.hetzner.com/storage/object-storage/overview
- Hetzner Docs: Object Lock और प्रतिधारण: https://docs.hetzner.com/storage/object-storage/howto-protect-objects/protect-object-lock-retention/
- Hetzner Docs: Load Balancer FAQ: https://docs.hetzner.com/cloud/load-balancers/faq
- Hetzner Docs: Networks FAQ: https://docs.hetzner.com/cloud/networks/faq
- Hetzner Docs: DNS (Console और API): https://docs.hetzner.com/networking/dns/overview/
- netcup: VPS G12.5 मूल्य: https://www.netcup.com/de/server/vps
- netcup: Root Server G12.5 मूल्य: https://www.netcup.com/de/server/root-server
- netcup Helpcenter: IP पते और फेलओवर: https://www.netcup.com/en/helpcenter/documentation/server/ip
- netcup: सर्वर एक्सटेंशन (फेलओवर IP मूल्य): https://www.netcup.com/de/server/kvm-server-erweiterungen
- netcup Helpcenter: फ़ायरवॉल (नीति "netcup Mail block"): https://www.netcup.com/en/helpcenter/documentation/server/firewall
- netcup Helpcenter: नेटवर्क / rDNS: https://www.netcup.com/en/helpcenter/documentation/server/network-server
- netcup: Storage Space (NFS): https://www.netcup.com/en/server/storage-space
- netcup Helpcenter: DNS API: https://www.netcup.com/en/helpcenter/documentation/domain/our-api
- STRATO: VPS Linux मूल्य: https://www.strato.de/server/linux-vserver/
- STRATO FAQ: VPS के लिए Reverse DNS: https://www.strato.de/faq/server/wie-kann-ich-reverse-dns-fuer-meinen-vps-vds-einrichten/
- STRATO FAQ: रूट सर्वर नेटवर्क फ़ायरवॉल (पोर्ट 25): https://www.strato.de/faq/server/wie-stelle-ich-die-netzwerk-firewall-bei-meinem-windows-root-server-ein/
- Cloudflare: Load Balancing ऐड-ऑन मूल्य: https://www.cloudflare.com/plans/network-cdn/
- Cloudflare Docs: Load Balancing सीमाएँ (हेल्थ चेक अंतराल): https://developers.cloudflare.com/load-balancing/reference/limitations/
- Cloudflare Docs: Load Balancing DNS रिकॉर्ड (MX/SRV केवल Enterprise): https://developers.cloudflare.com/load-balancing/load-balancers/dns-records/
- Cloudflare Docs: प्रॉक्सी मोड (DNS-only): https://developers.cloudflare.com/load-balancing/understand-basics/proxy-modes/
- Cloudflare Docs: प्रति प्लान Spectrum प्रोटोकॉल: https://developers.cloudflare.com/spectrum/protocols-per-plan/
- Cloudflare Docs: DNS TTL: https://developers.cloudflare.com/dns/manage-dns-records/reference/ttl/
- Cloudflare API: DNS रिकॉर्ड संपादित करें: https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/edit/
- deSEC: डोमेन और न्यूनतम TTL: https://desec.readthedocs.io/en/latest/dns/domains.html
- Akamai/Linode: IP फेलओवर (BGP, समान डेटा सेंटर): https://techdocs.akamai.com/cloud-computing/docs/configure-failover-on-a-compute-instance
- Akamai: NodeBalancer मूल्य: https://www.akamai.com/cloud/pricing/nodebalancers
- Vultr: BGP आवश्यकताएँ: https://docs.vultr.com/configuring-bgp-on-vultr
- RIPE NCC: AS नंबर का अनुरोध: https://www.ripe.net/manage-ips-and-asns/as-numbers/request-an-as-number/
- Proxmox VE: Cluster Manager (corosync लेटेंसी, QDevice): https://pve.proxmox.com/wiki/Cluster_Manager
- Proxmox Backup Server: डेटास्टोर (S3 सहित): https://pbs.proxmox.com/docs/storage.html
- WireGuard: https://www.wireguard.com/
- k3s: एम्बेडेड etcd के साथ HA: https://docs.k3s.io/datastore/ha-embedded
- k3s: वितरित हाइब्रिड या मल्टीक्लाउड क्लस्टर: https://docs.k3s.io/networking/distributed-multicloud
- etcd: ट्यूनिंग (heartbeat, election timeout): https://etcd.io/docs/v3.5/tuning/
- Talos Linux: KubeSpan: https://docs.siderolabs.com/talos/v1.11/networking/kubespan
- CloudNativePG: replica clusters: https://cloudnative-pg.io/docs/current/replica_cluster
- Patroni: डायनामिक कॉन्फ़िगरेशन (ttl, loop_wait, retry_timeout): https://patroni.readthedocs.io/en/latest/dynamic_configuration.html
- Patroni: प्रतिकृति मोड (synchronous_mode, strict): https://patroni.readthedocs.io/en/latest/replication_modes.html
- Patroni: DCS failsafe mode: https://patroni.readthedocs.io/en/latest/dcs_failsafe_mode.html
- PostgreSQL 18: सिंक्रोनस रेप्लिकेशन: https://www.postgresql.org/docs/current/warm-standby.html#SYNCHRONOUS-REPLICATION
- PostgreSQL 18: shared_buffers: https://www.postgresql.org/docs/current/runtime-config-resource.html
- pgBackRest (S3 और SFTP रिपॉज़िटरी): https://pgbackrest.org/
- CISA: डेटा बैकअप विकल्प (3-2-1): https://www.cisa.gov/sites/default/files/publications/data_backup_options.pdf
- Garage: प्रोजेक्ट पेज (मल्टी-डेटासेंटर, 200 ms / 50 Mbps): https://garagehq.deuxfleurs.fr/
- Garage: कॉन्फ़िगरेशन (replication_factor, कोरम): https://garagehq.deuxfleurs.fr/documentation/reference-manual/configuration/
- Garage: S3 संगतता (कोई Object Lock नहीं, कोई वर्शनिंग नहीं): https://garagehq.deuxfleurs.fr/documentation/reference-manual/s3-compatibility/
- Npgsql: फेलओवर और लोड बैलेंसिंग (मल्टी-होस्ट): https://www.npgsql.org/doc/failover-and-load-balancing.html
- Mermaid 12.2.0 (MIT), इस साइट पर vendored: https://www.npmjs.com/package/mermaid