// AWS · Custom Email Sender

Cognito Custom Email Sender: drei Fallen auf dem Weg zur Inbox

Cognito Custom Email Sender: three traps on the road to the inbox

29.04.2026 2026-04-29 Lesezeit ca. 10 Min ~10 min read AWS · Cognito · SES · KMS

Mein erster Versuch, eine Verifizierungs-Mail aus AWS Cognito zu schicken, war ein eigener SMTP-Server. Komplettes CDK-Stack, EC2-Instanz, Postfix-Konfiguration, alles. Lokal lief es. In der AWS-Cloud kam keine einzige Mail durch. Drei Fallen später lief das System produktiv. Hier die Geschichte, die Architektur, und was ich dabei gelernt habe.

My first attempt at sending a verification email from AWS Cognito was a full SMTP server of my own. A complete CDK stack, EC2 instance, Postfix configuration, the works. It ran locally. In the AWS cloud, not a single mail got through. Three traps later, the system was running in production. Here is the story, the architecture, and what I learned along the way.

Das DACH-Onboarding-Problem

The DACH onboarding problem

AWS Cognito kommt out-of-the-box mit funktionierendem E-Mail-Versand. Bei Sign-up, Verifizierung, Passwort-Reset versendet der Service automatisch. Klingt fertig. Bis du die erste Mail im Posteingang siehst.

AWS Cognito ships with working email delivery out of the box. On sign-up, verification, and password reset, the service sends mail automatically. Sounds done. Until you see the first mail in your inbox.

Absender: no-reply@verificationemail.com. Sprache: Englisch, hartcodiert. Layout: spartanischer Plain-Text mit einem Code. Branding: keines. Für ein DACH-SaaS-Produkt ist das ein Conversion-Killer. Ein Schweizer Anwender, der sich gerade bei deinem Compliance-Tool registriert hat, schaut auf eine englische Mail von einer Domain, die er nicht kennt, und überlegt, ob das Phishing ist. Die Verifizierungsrate fällt messbar.

Sender: no-reply@verificationemail.com. Language: English, hardcoded. Layout: spartan plain text with a code. Branding: none. For a SaaS product targeting the DACH region, that is a conversion killer. A Swiss user who just signed up for your compliance tool looks at an English mail from a domain they do not recognize and wonders whether it is phishing. The verification rate drops measurably.

Die Lösung steht in der AWS-Doku: Custom Email Sender Lambda. Cognito triggert eine Lambda-Funktion, die du selbst schreibst, und du übernimmst den Versand komplett. Eigene Domain, eigene Sprache, eigenes Template. Klingt geradlinig.

The solution is in the AWS docs: a Custom Email Sender Lambda. Cognito triggers a Lambda function that you write, and you take over delivery entirely. Your own domain, your own language, your own template. Sounds straightforward.

Es ist nicht geradlinig. Auf dem Weg zur produktiven Lösung warten drei Fallen, von denen die offizielle Doku zwei elegant verschweigt.

It is not straightforward. On the way to a production-ready setup, three traps wait, and the official documentation politely skips two of them.

Falle 1: Der SMTP-Server, der nie senden durfte

Trap 1: The SMTP server that was never allowed to send

Die erste Idee, wenn man E-Mail-Versand aus einer Lambda-Funktion bauen soll, ist meistens SMTP. Java hat JavaMail, Node hat Nodemailer, Python hat smtplib. Alles erprobt, alles dokumentiert, alles funktioniert lokal in zwei Stunden.

The first idea when you need to send email from a Lambda is usually SMTP. Java has JavaMail, Node has Nodemailer, Python has smtplib. All proven, all documented, all working locally in two hours.

Mein Reflex damals: einen schlanken SMTP-Server in der Cloud aufsetzen, gegen den die Lambda-Funktion sendet. Postfix auf einer kleinen EC2-Instanz, alles hinter VPC, sauber per CDK provisioniert. Ich habe den ganzen Stack geschrieben. Security Group, Elastic IP, Route 53 Eintrag, Postfix-Konfiguration über User Data. Deployment lief durch. Lambda gewireed. Sign-up-Flow ausgelöst.

My reflex at the time: spin up a lean SMTP server in the cloud and have the Lambda send to it. Postfix on a small EC2 instance, everything behind a VPC, cleanly provisioned via CDK. I wrote the whole stack. Security Group, Elastic IP, Route 53 entry, Postfix configuration via User Data. Deployment finished. Lambda wired. Sign-up flow triggered.

Nichts kam an.

Nothing arrived.

Was ich nicht wusste: AWS blockiert ausgehenden Port 25 by default, sowohl auf EC2 als auch auf Lambda. Das ist eine plattformweite Anti-Spam-Maßnahme, die seit Jahren existiert. Die offizielle Begründung: Spam-Prävention und Reputation-Schutz für die AWS-IP-Bereiche. Man kann den Block über AWS Support aufheben lassen, aber das ist ein eigener Antragsweg mit Begründung, Domain-Nachweis und Wartezeit. Für ein produktives System mit ohnehin geringer SMTP-Last ist das ein unnötiger Umweg.

What I did not know: AWS blocks outbound port 25 by default, both on EC2 and on Lambda. It is a platform-wide anti-spam measure that has been in place for years. The official rationale: spam prevention and reputation protection for the AWS IP ranges. You can have the block lifted via AWS Support, but that is its own path with justification, domain proof, and waiting time. For a production system with low SMTP volume anyway, that is an unnecessary detour.

Die Erkenntnis: SMTP aus AWS ist eine Sackgasse, wenn man nicht aus geschäftlichen Gründen einen eigenen Mail-Server betreiben muss. Für Transaktionsmails ist der Weg ein anderer.

The lesson: SMTP from AWS is a dead end unless you have a business reason to run your own mail server. For transactional mail, the path is a different one.

Lehre aus Falle 1 Wenn du auf AWS eine Standardlösung baust und sie funktioniert nicht, ist meistens nicht dein Code falsch. Es ist eine Plattform-Default-Einstellung, die die Doku zur primären Lösung nicht erwähnt, weil die primäre Lösung ein anderer AWS-Service ist.
Takeaway from trap 1 When you build a standard solution on AWS and it does not work, your code is usually not the problem. It is a platform default that the docs for the primary path do not mention, because the primary path is a different AWS service.

Falle 2: SES, aber nur über die API

Trap 2: SES, but only via the API

Der vorgesehene Weg auf AWS heißt Simple Email Service (SES). Vollständig managed, mit Domain-Verifizierung, DKIM, Bounce-Tracking, sandbox-Limit zum Start, Production-Antrag wenn man bereit ist. Das ist solide. Mein nächster Schritt war also: Lambda umschreiben, gegen SES senden statt gegen Postfix.

The intended AWS path is Simple Email Service (SES). Fully managed, with domain verification, DKIM, bounce tracking, a sandbox limit to start, and a production request when you are ready. Solid. My next step was rewriting the Lambda to send via SES instead of Postfix.

Nun bietet SES zwei Schnittstellen. Erstens SMTP über SES, also ein klassischer SMTP-Endpoint, gegen den man mit JavaMail oder Nodemailer arbeiten kann. Zweitens die SES API, die mit der AWS-SDK direkt aufgerufen wird, ohne SMTP-Protokoll dazwischen.

SES offers two interfaces. First, SMTP over SES, a classic SMTP endpoint you can hit with JavaMail or Nodemailer. Second, the SES API, called directly via the AWS SDK with no SMTP protocol in between.

Beim ersten Anlauf war SMTP-over-SES naheliegend, weil der bestehende Code von Falle 1 weiterverwendbar gewesen wäre. Nur: ich hoste in eu-central-2 (Zürich). Die Region ist seit 2022 verfügbar, und für Schweizer Datenschutzanforderungen ist sie der richtige Ort. Aber SES ist in eu-central-2 nur über die API verfügbar, nicht über SMTP-Endpoint. Der SMTP-Endpoint existiert in größeren Regionen wie eu-central-1 (Frankfurt) oder us-east-1, aber nicht in Zürich.

SMTP over SES was the obvious next step because the existing code from trap 1 would have been reusable. The catch: I host in eu-central-2 (Zurich). The region has been available since 2022, and for Swiss data protection requirements it is the right home. But SES in eu-central-2 is API-only. The SMTP endpoint exists in larger regions like eu-central-1 (Frankfurt) or us-east-1, but not in Zurich.

Damit war die Entscheidung wieder auf Code-Ebene zurückgeworfen: SES API direkt aus der Lambda. Was sich im Nachhinein als die bessere Wahl herausstellt. Die API-Variante ist schneller (keine SMTP-Handshake-Overhead), debuggbarer (saubere JSON-Responses statt SMTP-Status-Codes), und braucht keine zusätzlichen Credentials neben der IAM-Rolle der Lambda. Wer einmal SES per API integriert hat, will SMTP nicht mehr zurück.

That pushed the decision back to the code level: SES API directly from the Lambda. Which turned out to be the better choice anyway. The API variant is faster (no SMTP handshake overhead), more debuggable (clean JSON responses instead of SMTP status codes), and needs no additional credentials beyond the Lambda's IAM role. Once you have integrated SES via API, you do not want SMTP back.

Ein Detail, das man bei SES nicht überspringen sollte: jeder neue SES-Account startet im Sandbox-Modus. In der Sandbox kannst du nur an verifizierte Empfänger-Adressen senden, mit hartem Tageslimit. Für Produktion brauchst du den Antrag auf Production Access bei AWS Support, mit Beschreibung des Use Case, erwartetem Volumen, Bounce-Handling-Konzept. Der Antrag ist meistens innerhalb eines Tages durch, wenn das Konzept sauber ist. Nicht durch ist er, wenn man beim Bounce-Handling nichts sagen kann. Plant das ein, bevor das System live geht.

One detail you should not skip with SES: every new SES account starts in sandbox mode. In the sandbox you can only send to verified recipient addresses, with a hard daily limit. For production you need to request Production Access via AWS Support, including a use case description, expected volume, and a bounce handling concept. The request usually goes through within a day when the concept is clean. It does not go through when you have nothing to say about bounce handling. Plan this in before the system goes live.

Mails kamen jetzt an. Mit eigener Domain, eigenem Branding, deutschsprachigem Template. Sah aus, als wäre das System fertig.

Mails arrived now. With our own domain, our own branding, a localized template. Looked like the system was done.

Falle 3: KMS-Verschlüsselung trifft Cold Start

Trap 3: KMS encryption meets cold start

Eine Woche später kamen die ersten Support-Anfragen. Nutzer berichteten, sie hätten zwei Verifizierungs-Codes erhalten, manche sogar drei. Beide Codes funktionierten, aber das wirkte chaotisch und unprofessionell. In Logs der Lambda war zu sehen: dieselbe Sign-up-Anfrage wurde mehrfach getriggert, in Abständen von wenigen Sekunden.

A week later, the first support tickets came in. Users reported receiving two verification codes, some even three. Both codes worked, but it looked chaotic and unprofessional. The Lambda logs showed the same sign-up request triggering multiple times, seconds apart.

Hier kommt die Falle, die in fast keinem Tutorial vorkommt. Cognito übergibt der Custom Email Sender Lambda den Verifizierungs-Code nicht im Klartext. Der Code ist mit einem KMS-Schlüssel verschlüsselt, den du beim Anlegen des User Pools konfigurierst. Das macht Sinn aus Security-Sicht: der Code darf nicht in CloudWatch-Logs landen, falls jemand die Lambda-Logs einsieht.

Here comes the trap that almost no tutorial covers. Cognito does not hand the verification code to your Custom Email Sender Lambda in plain text. The code is encrypted with a KMS key that you configure when you set up the User Pool. That makes sense from a security perspective: the code must not end up in CloudWatch logs if someone inspects the Lambda logs.

Zum Entschlüsseln reicht aber nicht der einfache kms.Decrypt-Aufruf, weil Cognito mit einem Envelope-Encryption-Verfahren arbeitet. Du brauchst das AWS Encryption SDK, ein deutlich umfangreicheres Paket mit eigenen Materials Providers, Keyrings und Cryptographic Materials Manager. Auf der JVM bringt diese Library einen erheblichen Klassen-Footprint mit. Beim Cold Start einer Java Lambda muss all das geladen, initialisiert, verifiziert werden. Cold Starts mit Encryption SDK auf JVM bewegen sich erfahrungsgemäß im Bereich mehrerer Sekunden, je nach Memory-Setting und Region.

Decryption needs more than a plain kms.Decrypt call because Cognito uses envelope encryption. You need the AWS Encryption SDK, a much heavier package with its own Materials Providers, Keyrings, and Cryptographic Materials Manager. On the JVM, this library brings a significant class footprint. During a Java Lambda cold start, all of that needs to load, initialize, and verify. In practice, cold starts with the Encryption SDK on JVM land in the multi-second range, depending on memory setting and region.

Cognito hat eine eigene Retry-Logik für Custom Email Sender Lambdas. Wenn die Lambda nicht innerhalb eines bestimmten Zeitfensters antwortet, ruft Cognito sie nochmal auf. Und nochmal. Bis es klappt oder das Limit erreicht ist. Bei einer langsamen JVM-Lambda mit Encryption SDK heißt das: erste Invocation läuft noch, zweite startet, beide schicken am Ende eine Mail, der Nutzer bekommt zwei Codes. Im schlimmsten Fall sogar drei, wenn die Lambda parallel skaliert.

Cognito has its own retry logic for Custom Email Sender Lambdas. If the Lambda does not respond within a certain time window, Cognito calls it again. And again. Until it succeeds or the limit is reached. For a slow JVM Lambda with the Encryption SDK, that means the first invocation is still running, the second one starts, both send a mail, and the user receives two codes. In the worst case three, if the Lambda scales out in parallel.

Aus Cognito-Sicht ist die Retry-Logik korrekt. Cognito kann nicht wissen, ob die Lambda gerade nur lange braucht, oder ob sie wirklich abgestürzt ist und die Mail nie verschickt wird. Im Zweifel lieber ein Retry, damit der Nutzer nicht ohne Code dasteht. Nur ist die Logik unter der Annahme gebaut, dass die Lambda im üblichen Bereich antwortet, also unter einer Sekunde.

From Cognito's perspective, the retry logic is correct. Cognito cannot know whether the Lambda is just taking a while or whether it actually crashed and the mail will never go out. When in doubt, better one retry than a user without a code. The logic is built on the assumption that the Lambda answers in the usual range, under a second.

Die offensichtliche Lösung wäre, die Lambda warm zu halten (Provisioned Concurrency, scheduled Pings). Beides funktioniert, beides kostet zusätzlich, und beides löst nicht den initialen Cold Start nach einem Deployment, oder den Fall, dass die Lambda parallel skaliert und neue Instanzen kalt starten. Die robustere Lösung ist, die Lambda so zu bauen, dass sie gar keinen relevanten Cold Start hat.

The obvious fix would be keeping the Lambda warm (Provisioned Concurrency, scheduled pings). Both work, both cost extra, and neither solves the initial cold start after a deployment, or the case where the Lambda scales out and new instances start cold. The more robust fix is to build the Lambda so it has no relevant cold start to begin with.

Mein Stack dafür: Quarkus Native. Java-Code, mit GraalVM zu einem Native Binary kompiliert. Cold Start im niedrigen dreistelligen Millisekunden-Bereich, deutlich unterhalb des Cognito-Retry-Fensters. Das gleiche Prinzip gilt für andere Native-Frameworks (Micronaut, Spring Native), oder alternativ Node.js und Python, die ohne JVM-Warm-up auskommen.

My stack for this: Quarkus Native. Java code compiled to a native binary with GraalVM. Cold start in the low three-digit milliseconds range, well under the Cognito retry window. The same principle holds for other native frameworks (Micronaut, Spring Native), or alternatively for Node.js and Python, which do not need JVM warm-up.

Eine Detailfeinheit verdient Erwähnung: Native-Compilation und das AWS Encryption SDK vertragen sich nicht out-of-the-box. Die Kombination braucht zusätzliche Konfiguration für die Reflection und einige Klassen-Initialisierungen, die man in Tutorials selten dokumentiert findet. Wer den Weg geht, sollte mit etwas Trial-and-Error rechnen, vor allem bei der ersten Native Image Build. Wenn es dann läuft, läuft es schnell und stabil.

One detail deserves a mention: native compilation and the AWS Encryption SDK do not get along out of the box. The combination needs additional configuration for reflection and a handful of class initializations that tutorials rarely document. Anyone going down this path should expect some trial and error, especially during the first Native Image build. Once it runs, it runs fast and stable.

Idempotenz als zweite Verteidigungslinie

Idempotency as a second line of defense

Selbst mit einer schnellen Lambda kann der Retry-Fall in Spitzenlast-Momenten auftreten. Eine zweite Verteidigungslinie ist deshalb sinnvoll: die Mail-Versand-Logik idempotent zu bauen. Konkret heißt das, anhand von Cognito-Trigger-Source und der User-Identity zu prüfen, ob in den letzten Sekunden bereits eine Mail mit demselben Code rausging. Eine kleine DynamoDB-Tabelle mit TTL reicht dafür, oder eine Markierung in einem bestehenden User-Record. Aufwand gering, Wirkung hoch: doppelte Mails sind in der Architektur ausgeschlossen, nicht nur unwahrscheinlich.

Even with a fast Lambda, the retry case can still happen during peak load. A second line of defense is worthwhile: making the mail-sending logic idempotent. Concretely, that means checking via the Cognito trigger source and user identity whether a mail with the same code went out in the last few seconds. A small DynamoDB table with TTL is enough, or a marker on an existing user record. Low effort, high impact: duplicate mails are ruled out by the architecture, not just unlikely.

In der Praxis ist die Kombination aus schneller Lambda und Idempotenz-Check der robuste Stand. Nur eine schnelle Lambda allein verlässt sich darauf, dass Cognito unter keinen Umständen retried, was nicht garantiert ist. Nur Idempotenz ohne schnelle Lambda erzeugt zwar keine doppelten Mails mehr, aber jede zweite Invocation läuft trotzdem voll durch und kostet Lambda-Zeit. Beides zusammen ist sauber.

In practice, fast Lambda plus idempotency check is the robust position. Fast Lambda alone relies on Cognito never retrying, which is not guaranteed. Idempotency alone stops duplicate mails but every second invocation still runs fully and costs Lambda time. Both together is clean.

Was die Doku weglässt Der KMS-Encryption-Step für den Verifizierungs-Code ist in den AWS-Cognito-Docs als Detail beschrieben, nicht als zentrale Hürde. Tutorials und Blog-Artikel zeigen das fast immer mit JVM-Lambdas und ohne Hinweis auf das Cold-Start-Problem im Zusammenspiel mit der Cognito-Retry-Logik. Wer das in Production betreibt, lernt es am Support-Ticket-Volumen.
What the docs leave out The KMS encryption step for the verification code is described in the AWS Cognito docs as a detail, not as a central hurdle. Tutorials and blog posts almost always show this with JVM Lambdas and without a note on the cold start problem interacting with the Cognito retry logic. Anyone running this in production learns it through their support ticket volume.

Architektur-Übersicht

Architecture overview

Die Bausteine, die am Ende produktiv laufen:

The components that end up running in production:

Architektur: Cognito triggert die Custom Email Sender Lambda mit verschlüsseltem Verifizierungs-Code, die Lambda entschlüsselt via KMS (AWS Encryption SDK) und versendet über die SES API.
// Cognito triggert eine Lambda mit verschlüsseltem Code, Lambda entschlüsselt via KMS und versendet via SES API
// Cognito triggers a Lambda with an encrypted code, the Lambda decrypts via KMS and sends via the SES API

Der Flow im Detail. Der Nutzer registriert sich über deine Anwendung gegen Cognito (1). Cognito generiert einen Verifizierungs-Code, verschlüsselt ihn mit dem konfigurierten KMS-Key, und triggert die Custom Email Sender Lambda mit dem verschlüsselten Payload (2). Die Lambda entschlüsselt den Code über das AWS Encryption SDK (3), baut die Mail mit dem deutschen Template, und ruft SES.SendEmail auf (4). SES stellt die Mail über die verifizierte Domain zu (5).

The flow in detail. The user signs up through your app against Cognito (1). Cognito generates a verification code, encrypts it with the configured KMS key, and triggers the Custom Email Sender Lambda with the encrypted payload (2). The Lambda decrypts the code via the AWS Encryption SDK (3), builds the mail with the localized template, and calls SES.SendEmail (4). SES delivers the mail through the verified domain (5).

Vier Komponenten, drei davon sind AWS-managed, eine ist eigener Code. Genau dieser eine Code-Anteil entscheidet, ob das System produktiv tragfähig ist.

Four components, three of them AWS-managed, one is your own code. That one code share decides whether the system holds up in production.

Trade-offs: welche Runtime für die Lambda

Trade-offs: which runtime for the Lambda

Die Wahl der Runtime ist die einzige nicht-triviale Architektur-Entscheidung im ganzen Setup. Eine grobe Übersicht:

The runtime choice is the only non-trivial architecture decision in the whole setup. A rough overview:

Option
Cold Start
Encryption SDK
Empfehlung
Java JVM
mehrere s
native
vermeiden
Java Native
unter 500ms
Reflection-Config
empfohlen
Node.js
unter 500ms
JS-SDK verfügbar
empfohlen
Python
unter 500ms
pip Paket
empfohlen
Option
Cold start
Encryption SDK
Recommendation
Java JVM
several s
native
avoid
Java Native
under 500ms
reflection config
recommended
Node.js
under 500ms
JS SDK available
recommended
Python
under 500ms
pip package
recommended

Die Tabelle ist eine Faustregel, keine Messreihe. Tatsächliche Cold-Start-Zeiten hängen von Memory-Setting, Region, Paketgröße und VPC-Konfiguration ab. Wer eine Java-Codebasis und ein Java-Team hat, fährt mit Native gut. Wer von grüner Wiese startet, kann sich Node.js oder Python sparen die Reflection-Diskussion.

The table is a rule of thumb, not a benchmark. Actual cold start times depend on memory setting, region, package size, and VPC configuration. With an existing Java codebase and a Java team, native is the right call. Starting from a clean slate, Node.js or Python skip the reflection discussion entirely.

Eine echte Messreihe steht auf meinem Plan. Ich arbeite an einem Cold-Start-Benchmark, der Quarkus Native, Java JVM und Node.js unter denselben Bedingungen vergleicht: gleiches Memory-Setting, gleiche Region (eu-central-2), gleicher Workload mit Encryption SDK. Sobald die Zahlen stehen, gibt es einen eigenen Beitrag dazu, mit reproduzierbarem Setup und Repository.

A real benchmark is on my list. I am working on a cold start benchmark that compares Quarkus Native, Java JVM, and Node.js under the same conditions: same memory setting, same region (eu-central-2), same workload with the Encryption SDK. Once the numbers are in, a separate post will follow with a reproducible setup and a public repository.

Wann braucht man das nicht?

When you do not need this

Wenn dein Produkt englischsprachig, US-zentriert und ohne Branding-Anspruch ist, reicht der Cognito-Default. Wenn du nur intern nutzt und keine externen Empfänger hast, ist die Frage egal. Die Custom Email Sender Lambda ist die Antwort auf konkretes Marketing- und Compliance-Bedürfnis: lokalisiert, gebrandet, datenschutzkonform absendend von der eigenen Domain.

If your product is English-only, US-centric, and has no branding requirements, the Cognito default is fine. If you only use it internally with no external recipients, the question is moot. The Custom Email Sender Lambda answers a concrete marketing and compliance need: localized, branded, privacy-compliant delivery from your own domain.

Das größere Muster

The bigger pattern

Die Geschichte hat eine Pointe, die über Cognito hinausgeht. Drei Mal in diesem Projekt war mein erster Reflex, etwas selbst zu bauen. Einen SMTP-Server. Einen SMTP-Client gegen SES. Eine schnelle JVM-Lambda mit aufwendigem Warm-up. Drei Mal war die bessere Lösung, einen AWS-managed-Service zu nutzen oder ein etabliertes Framework korrekt einzusetzen, statt eigenen Code anzuhäufen.

The story has a point that goes beyond Cognito. Three times in this project, my first reflex was to build something myself. An SMTP server. An SMTP client against SES. A fast JVM Lambda with elaborate warm-up. Three times the better solution was to use an AWS-managed service or apply an established framework correctly, instead of accumulating my own code.

Das ist kein Cognito-spezifisches Phänomen. Es ist das wiederkehrende Muster bei Cloud-Architektur. AWS bietet mehrere hundert Services, und die meisten existieren, weil ein konkretes Problem in einer Größenordnung auftritt, die DIY unwirtschaftlich macht. Auth, Mail-Versand, Verschlüsselung, Queue-Verarbeitung. Wer das selbst baut, baut Software, die jemand anders schon gebaut hat, und übernimmt die Wartung dafür.

That is not a Cognito-specific phenomenon. It is the recurring shape of cloud architecture. AWS offers several hundred services, and most exist because a concrete problem occurs at a scale that makes DIY uneconomic. Auth, mail delivery, encryption, queue processing. Building it yourself means building software that someone else has already built, and taking on the maintenance for it.

In einem früheren Beitrag habe ich denselben Punkt für Authentifizierung gemacht: Logins baut man nicht selbst. Cognito plus Authorization@Edge plus API Gateway lösen das Problem in einem Bruchteil der Zeit, mit deutlich besserer Sicherheit. Hier ist die Variante davon für E-Mail-Versand: Cognito plus SES plus KMS, dazu eine schlanke Lambda als Klebstoff. Der eigene Code-Anteil schrumpft auf das, was wirklich produktspezifisch ist. Die Templates, die deutsche Übersetzung, die Branding-Entscheidungen.

In an earlier post I made the same point for authentication: do not roll your own logins. Cognito plus Authorization@Edge plus API Gateway solve the problem in a fraction of the time, with significantly better security. Here is the equivalent for email delivery: Cognito plus SES plus KMS, with a lean Lambda as glue. The custom code share shrinks to what is genuinely product-specific. The templates, the localization, the branding decisions.

Die Faustregel, die sich daraus ergibt: jeder Service, den AWS anbietet, ist eine Frage. Brauche ich diese Funktion? Wenn ja, fast immer ist die Antwort, den Service zu nutzen, nicht ihn nachzubauen. Die Ausnahmen lassen sich an einer Hand abzählen, und sie sind meistens regulatorischer Natur, nicht technischer.

The rule of thumb that emerges: every AWS service is a question. Do I need this capability? If yes, the answer is almost always to use the service, not rebuild it. The exceptions can be counted on one hand, and they are usually regulatory, not technical.

// next Cold-Start-Benchmark: Quarkus Native vs Java JVM vs Node.js auf Lambda (eu-central-2) · demnächst Cold start benchmark: Quarkus Native vs Java JVM vs Node.js on Lambda (eu-central-2) · coming soon

References

References

AWS & Cloud Security

Dieselben Fallen im eigenen Projekt?

Hitting the same traps in your project?

Ich baue AWS-Serverless-Architekturen für Schweizer SaaS-Produkte, von Cognito-Onboarding bis SES-Versand. Wer an einer dieser Stellen festhängt, kann mich für ein Build & Improve Sprint anfragen.

I build AWS serverless architectures for Swiss SaaS products, from Cognito onboarding to SES delivery. If you are stuck at one of these points, you can reach out for a Build & Improve Sprint.

Gespräch buchen → Book a call →
// alle Beiträge // all posts Istvan Kappelmayer · K-I-Soft