{"id":29224,"date":"2026-09-12T08:00:00","date_gmt":"2026-09-12T06:00:00","guid":{"rendered":"https:\/\/blog.mi.hdm-stuttgart.de\/?p=29224"},"modified":"2026-09-11T21:47:47","modified_gmt":"2026-09-11T19:47:47","slug":"studyswipe-in-aws","status":"publish","type":"post","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/","title":{"rendered":"StudySwipe in AWS"},"content":{"rendered":"\n<div class=\"wp-block-jetpack-markdown\"><p>StudySwipe ist eine Karteikarten-Anwendung: Man legt Ordner an, f\u00fcllt sie mit Karten und lernt sie im Swipe-Prinzip durch. Technisch besteht es aus einem Spring-Boot-Backend, einem React-Frontend und einer PostgreSQL-Datenbank. Entstanden ist es als Studienprojekt im dritten Semester, bis dahin lief es ausschlie\u00dflich lokal \u00fcber Docker Compose.<\/p>\n<h2>Ausgangslage<\/h2>\n<p>F\u00fcr den Kurs Softw. Dev. Cloud Computing habe ich mich f\u00fcr ein Projekt entschieden, das folgendes Ziel hatte. Dieses Ziel hab ich mir am Anfang so notiert:<\/p>\n<\/div>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\u201e1x Script laufen lassen und alles hochfahren k\u00f6nnen.&#8221;<\/p>\n<\/blockquote>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>Was ich zu dem Zeitpunkt wusste war was eine EC2-Instanz grob ist, aber ich wusste nicht wie eine EC2-Instanz mit Docker zusammenspielt. Wie kommt ein Container, den ich lokal gebaut habe, \u00fcberhaupt auf die Maschine? Und was passiert eigentlich mit meiner <strong>docker-compose.yml<\/strong>, die bisher nur lokal existiert hat? Was haben AWS &amp; Terraform damit zu tun?<\/p>\n<p>Genau diese Fragen zu beantworten war r\u00fcckblickend der eigentliche Kern des Projekts.<\/p>\n<h2>Warum StudySwipe<\/h2>\n<p>Urspr\u00fcnglich wollte ich ein Projekt namens Aviation Photo nehmen, eine Datenbank f\u00fcr Flugzeugfotos mit Suche und Filtern.<\/p>\n<p>Ich habe w\u00e4hrend der Vorlesungen f\u00fcr Aviation Photo die Vorbereitungen getroffen, mich mit dem Projekt und AWS so vertraut wie m\u00f6glich gemacht, denn f\u00fcr dieses Projekt existierte bereits ein AWS Konto mit getrennter PROD\/TEST-Umgebung in produktiver Nutzung. In der letzten Vorlesung habe ich dann zusammen mit meinem Dozenten meine Optionen angeschaut, um mein Ziel zu erreichen. Dabei sind wir zu dem Entschluss gekommen, dass dieses Projekt nicht ideal daf\u00fcr geeignet ist. Da ich nicht vertraut war mit dem Tech-Stack, als auch das Projekt noch viele Fragezeichen aufgeschmissen hat. Ebenso w\u00fcrde ich nicht ohne Risiko arbeiten und eventuell etwas zerst\u00f6ren, wenn ich in einer bereits laufenden Umgebung arbeiten w\u00fcrde. Mit der gr\u00f6\u00dfte Punkt f\u00fcr StudySwipe war, dass StudySwipe bereits in GitLab versioniert war, mein SE3 Projekt war, der Tech-Stack (Java\/Spring Boot) war mir vertraut, und es fehlte genau das was ich mir als Ziel gesetzt habe: eine Deployment-Pipeline.<\/p>\n<p>Der direkte Vergleich der beiden Architekturen zeigt, wo ich bewusst einfacher geblieben bin:<\/p>\n<\/div>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><\/td><td><strong>Aviation Photo<\/strong><\/td><td><strong>StudySwipe<\/strong><\/td><\/tr><tr><td>Datenbank<\/td><td>RDS My SQL (managed)<\/td><td>Postgres im eigenen Container<\/td><\/tr><tr><td>Storage<\/td><td>S3 f\u00fcr Bilder<\/td><td>nicht ben\u00f6tigt<\/td><\/tr><tr><td>Domain\/HTTPS<\/td><td>Route 53 + Let&#8217;s Encrypt <\/td><td>nur IP, kein HTTPS<\/td><\/tr><tr><td>Umgebungen<\/td><td>PROD + TEST getrennt<\/td><td>eine einzige Instanz<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<div class=\"wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-f56f613f wp-block-group-is-layout-flex\">\n<div class=\"wp-block-jetpack-markdown wp-container-content-9cfa9a5a\"><p>Aviation Photo hat eine deutlich produktionsn\u00e4here Infrastruktur. F\u00fcr mein Kurs-Projekt h\u00e4tten RDS und eine eigene Domain zus\u00e4tzliche Komplexit\u00e4t bedeutet, ohne dass ich dadurch mehr \u00fcber das gelernt h\u00e4tte, worum es mir ging. Ich wollte bei den Basics anfangen und verstehen, was jeder einzelne Baustein tut, statt m\u00f6glichst viele AWS-Dienste zu benutzen. Die rechte Spalte ist damit weniger eine Liste von L\u00fccken als eine bewusste Reihenfolge: erst das Fundament, der Rest kann sp\u00e4ter drauf.<\/p>\n<h2>Erstmal: ein eigener AWS-Account<\/h2>\n<p>Bevor \u00fcberhaupt Code ins Spiel kam, brauchte ich eine Umgebung, in der ich gefahrlos experimentieren konnte. Zugriff auf ein bestehendes AWS-Konto hatte ich zwar, aber dort lief bereits etwas produktiv. Kein guter Ort, um Dinge auszuprobieren, deren Auswirkungen man noch nicht absch\u00e4tzen kann.<\/p>\n<p>Also eigener Account, und dann der Reihe nach das N\u00f6tigste: Root-Zugang mit Zwei-Faktor-Authentifizierung absichern, einen Budget-Alarm f\u00fcr den Fall, dass ich versehentlich etwas Teures laufen lasse, einen separaten IAM-Benutzer f\u00fcr die t\u00e4gliche Arbeit statt alles \u00fcber den Root-Account zu machen, und die AWS CLI lokal konfigurieren, damit Terraform sp\u00e4ter \u00fcberhaupt mit dem Konto sprechen kann.<\/p>\n<p>Klingt nach Formalit\u00e4ten, war aber r\u00fcckblickend eine der besseren Entscheidungen: Alles, was danach kam, inklusive mehrfachem kompletten L\u00f6schen und Neuaufbauen der gesamten Infrastruktur, konnte ich ohne Sorge machen. Im schlimmsten Fall w\u00e4re nur mein eigener Testaccount betroffen gewesen.<\/p>\n<h2>Phase 1: Erstmal alles von Hand<\/h2>\n<p>Bevor ich irgendwas automatisiert hab, wollte ich die App einmal manuell auf einer EC2-Instanz zum Laufen bringen. Terraform hat mir daf\u00fcr eine leere Instanz erzeugt, den Rest hab ich per SSH selbst gemacht: Java installieren, Docker installieren, Postgres als Container starten, die Anwendung starten.<\/p>\n<p>Das war der Punkt, an dem ich das erste Mal richtig h\u00e4ngen geblieben bin. Die Anwendung fuhr hoch, aber ohne Datenbank-Anbindung:<\/p>\n<pre><code class=\"language-bash\" data-line=\"\">No bean named &#039;entityManagerFactory&#039; available\n<\/code><\/pre>\n<p>Die Fehlermeldung selbst f\u00fchrt erstmal in die Irre, sie klingt nach einem Konfigurationsfehler in der Anwendung. Tats\u00e4chlich lag es am Build: Das Projekt hatte einen eigenen, selbstgebauten Mechanismus, um das Backend-Docker-Image zu erzeugen. Dieser hat alle Bibliotheken entpackt und in einen gemeinsamen Ordner zusammengef\u00fchrt, mit der Einstellung, bei Namenskonflikten einfach eine Version zu behalten und den Rest zu verwerfen:<\/p>\n<pre><code class=\"language-bash\" data-line=\"\">duplicatesStrategy(DuplicatesStrategy.EXCLUDE)\n<\/code><\/pre>\n<p>Das Problem: Mehrere Spring-Bibliotheken bringen jeweils eine Datei mit demselben Namen mit, eine Art Inhaltsverzeichnis, das Spring Boot sagt, welche Funktionen es beim Start automatisch aktivieren soll. Beim Zusammenf\u00fchren hat also nur eine dieser Dateien \u00fcberlebt, die anderen wurden stillschweigend weggeworfen. Darunter die f\u00fcr die Datenbank-Anbindung. Kein Fehler beim Bauen, keine Warnung, die App startete einfach unvollst\u00e4ndig.<\/p>\n<p>Nachweisen lie\u00df sich das mit einem Blick in die fertige jar-Datei. Der folgende Befehl listet ihren Inhalt auf und z\u00e4hlt, wie viele Eintr\u00e4ge davon im Bibliotheks-Ordner liegen:<\/p>\n<pre><code class=\"language-bash\" data-line=\"\">$ unzip -l application-plain.jar | grep -c &#039;BOOT-INF\/lib\/&#039;\n0\n<\/code><\/pre>\n<p>Null Bibliotheken. Nach dem Fix, mit Spring Boots eigenem Build-Mechanismus\n(<strong>bootJar<\/strong> statt des selbstgebauten <strong>jar<\/strong>-Tasks), derselbe Befehl:<\/p>\n<pre><code class=\"language-bash\" data-line=\"\">$ unzip -l application-plain.jar | grep -c &#039;BOOT-INF\/lib\/&#039;\n73\n<\/code><\/pre>\n<p>73 Bibliotheken, darunter der Postgres-Treiber, Hibernate und die\nSpring-Autokonfiguration, also genau das, was vorher gefehlt hat.<\/p>\n<p>Der Fix selbst waren am Ende zwei ge\u00e4nderte Zeilen:<\/p>\n<pre><code class=\"language-groovy\" data-line=\"\">- copyFile jar.archiveFileName.get(), &#039;\/app\/test_service.jar&#039;\n+ copyFile bootJar.archiveFileName.get(), &#039;\/app\/test_service.jar&#039;\n<\/code><\/pre>\n<pre><code class=\"language-groovy\" data-line=\"\">- from jar.destinationDirectory\n+ from bootJar.destinationDirectory\n<\/code><\/pre>\n<p>Beide Stellen verwiesen auf den Output des selbstgebauten <strong>jar<\/strong>-Tasks statt auf den von <strong>bootJar<\/strong>. Weil beide Varianten zuf\u00e4llig denselben Dateinamen erzeugen, ist nie aufgefallen, dass die falsche Datei ins Image gewandert ist. Der Weg dorthin war deutlich l\u00e4nger als der Fix selbst.<\/p>\n<h2>Phase 2: Terraform und die Frage, was eigentlich wohin geh\u00f6rt<\/h2>\n<p>Terraform war von Anfang an dabei, die leere EC2-Instanz kam ja schon per <strong>terraform apply<\/strong>. Was ich in dieser Phase gelernt hab, war weniger die Syntax als die Frage, <strong>welche Ebene wof\u00fcr zust\u00e4ndig ist<\/strong>.<\/p>\n<p>Terraform beschreibt Infrastruktur: welche Maschine, welches Betriebssystem-Abbild, welche Netzwerk-Regeln, welche Zugriffsrechte. Was auf dieser Maschine dann l\u00e4uft, ist eine andere Ebene und daf\u00fcr gibt es in Terraform ein Feld namens <strong>user_data<\/strong>: ein Skript, das beim allerersten Hochfahren der Instanz automatisch ausgef\u00fchrt wird.<\/p>\n<p>Dort kam alles, was ich in Phase 1 manuell gemacht hatte rein. Der Ablauf verschob sich damit von \u201cich logge mich ein und richte ein\u201d zu \u201cdie Instanz richtet sich beim Booten selbst ein\u201d.<\/p>\n<p>Eine Eigenheit davon hat mich sp\u00e4ter nochmal besch\u00e4ftigt: <strong>user_data<\/strong> l\u00e4uft <strong>nur beim allerersten Boot<\/strong>. \u00c4ndere ich das Skript, passiert auf einer bereits laufenden Instanz gar nichts, Terraform speichert den neuen Wert zwar, ausgef\u00fchrt wird er aber nie. Um \u00c4nderungen daran zu testen, muss man die Instanz gezielt neu bauen lassen:<\/p>\n<pre><code class=\"language-bash\" data-line=\"\">terraform apply -replace=&quot;aws_instance.studyswipe_server&quot;\n<\/code><\/pre>\n<p>In dieser Phase ist mir auch das erste Mal etwas passiert, das sich sp\u00e4ter wiederholt hat: Ich hatte <strong>postgres:latest<\/strong> verwendet, und <strong>latest<\/strong> zeigte inzwischen auf eine neue Hauptversion mit ge\u00e4ndertem internen Speicherformat:<\/p>\n<pre><code class=\"language-bash\" data-line=\"\">Error: in 18+, these Docker images are configured to store database\ndata in a format which is compatible with &quot;pg_ctlcluster&quot;\n<\/code><\/pre>\n<p>Ich hatte an meinem Setup nichts ge\u00e4ndert, aber die Umgebung hat sich unter mir ver\u00e4ndert. Dasselbe Muster kam sp\u00e4ter nochmal, als das <strong>gradle:alpine<\/strong>-Image in der CI-Pipeline eine viel neuere Gradle-Version enthielt als lokal und ein Build fehlschlug.<\/p>\n<h2>Phase 3: Docker-Compose und ECR<\/h2>\n<p>Bis hierhin lief auf der Instanz eine Mischung, die ich mir selbst zusammengesetzt hatte: Backend direkt als systemd-Dienst, nur die Datenbank in einem Container, Nginx auf dem Host. Das funktionierte, war aber nicht die Struktur, die das Original-Projekt vorsah. Dort waren von Anfang an vier Container geplant: Datenbank, Backend, Frontend und Nginx, alle \u00fcber docker-compose.<\/p>\n<p>Ich hab mich f\u00fcr den Umbau entschieden, obwohl das, was ich hatte, bereits lief. Das war keine leichte Entscheidung.<\/p>\n<p><strong>Dagegen sprach:<\/strong> Das bestehende Setup lief nachweislich, war getestet, und der Umbau bedeutete, Backend und Frontend erst zu containerisieren, ECR einzurichten und dabei den Fat-Jar-Bug im Original-Build zu finden. Arbeit f\u00fcr etwas, das schon funktionierte.<\/p>\n<p><strong>Daf\u00fcr sprach:<\/strong> Das Original-Projekt war von Anfang an so gedacht. Ein einziger <strong>docker-compose up<\/strong> als Startbefehl liegt n\u00e4her an meinem Ziel als vier verschiedene Mechanismen nebeneinander (systemd f\u00fcrs Backend, Docker f\u00fcr die Datenbank, Nginx auf dem Host). Und f\u00fcr die Automatisierung wird das Provisionierungs-Skript dadurch deutlich einfacher: statt Java, Docker, Nginx und systemd einzeln zu installieren und zu konfigurieren, reicht \u201cDocker installieren, <strong>docker-compose up<\/strong>\u201d. Die ganze Komplexit\u00e4t steckt dann im Compose-File statt im Skript.<\/p>\n<p>Damit kam die Frage, die vorher gar nicht existiert hatte: Wie kommen vier fertig gebaute Container-Images automatisch auf eine neue Instanz? Genau hier hat sich die anf\u00e4ngliche Wissensl\u00fccke geschlossen. Die Antwort ist ECR, eine Container-Registry bei AWS. Ich baue die Images lokal, lade sie einmal hoch, und die Instanz zieht sie sich beim Booten selbst. Authentifiziert \u00fcber eine IAM-Rolle, ohne dass irgendwo ein Passwort im Skript steht.<\/p>\n<p>Damit war auch klar, wie sich lokale und entfernte Docker-Umgebung zueinander verhalten. Zwei komplett getrennte Docker-Installationen, die nichts voneinander wissen. Die eine baut, die andere f\u00fchrt aus. ECR ist das Bindeglied dazwischen.<\/p>\n<h2>Das Timing-Rennen mit ECR<\/h2>\n<p>Nach dem ersten kompletten <strong>terraform destroy<\/strong> und <strong>terraform apply<\/strong> kam dieser Fehler:<\/p>\n<pre><code class=\"language-bash\" data-line=\"\">ERROR: for backend  failed to resolve reference\n&quot;...studyswipe-backend:latest&quot;: not found\n<\/code><\/pre>\n<p>Die Ursache: Registry (ECR) und Server (EC2) lagen im selben Terraform-Projekt. Ein einziger <code class=\"\" data-line=\"\">apply<\/code> hat beides gleichzeitig erzeugt. Die Instanz war nach etwa anderthalb Minuten fertig gebootet und wollte sofort die Images abholen, aber die musste ich erst manuell hochladen und das dauerte l\u00e4nger, weil das Frontend-Image fast 800 MB gro\u00df ist.<\/p>\n<p>Der erste Gedanke war, schnell nachzupushen, w\u00e4hrend die Instanz noch bootete. Das hat funktioniert. Aber es war offensichtlich keine L\u00f6sung, sondern ein Test um zu sehen ob alles funktioniert soweit. Denn das n\u00e4chste Mal mit langsamerem Internet oder gr\u00f6\u00dferem Image h\u00e4tte es wieder nicht geklappt.<\/p>\n<p>Die eigentliche L\u00f6sung kam, als klar wurde, warum das Rennen \u00fcberhaupt entstehen konnte. Zwei Dinge mit v\u00f6llig unterschiedlicher \u00c4nderungsgeschwindigkeit lagen im selben Terraform-Zustand. Die Registry \u00e4ndert sich fast nie, die Instanz wird bei jedem Test neu gebaut, die Images \u00e4ndern sich bei jedem Code-Release.<\/p>\n<p>Ich hab beides in zwei getrennte Terraform-Projekte aufgeteilt:<\/p>\n<pre><code class=\"\" data-line=\"\">terraform\/\n\u251c\u2500\u2500 registry\/          \u2190 nur die ECR-Repositories\n\u2502   \u251c\u2500\u2500 main.tf\n\u2502   \u2514\u2500\u2500 outputs.tf\n\u2514\u2500\u2500 infrastructure\/    \u2190 Netzwerk, IAM, EC2, Elastic IP\n    \u251c\u2500\u2500 main.tf\n    \u251c\u2500\u2500 iam.tf\n    \u251c\u2500\u2500 user_data.sh.tpl\n    \u2514\u2500\u2500 ...\n<\/code><\/pre>\n<p>In <strong>infrastructure<\/strong> werden die Repositories nicht mehr erzeugt, sondern nur noch nachgeschlagen:<\/p>\n<pre><code class=\"language-hcl\" data-line=\"\">data &quot;aws_ecr_repository&quot; &quot;backend&quot; {\n  name = &quot;studyswipe-backend&quot;\n}\n<\/code><\/pre>\n<p>Der Unterschied ist nicht die Ordnerstruktur an sich, sondern dass sie die richtige Reihenfolge erzwingt: erst Registry bauen, dann in Ruhe die Images hochladen, danach erst die Instanz starten, die garantiert etwas vorfindet. Vorher war diese Reihenfolge nur etwas, das ich mir selbst merken musste und deshalb vergessen konnte.<\/p>\n<h2>Kleinere Stolpersteine<\/h2>\n<p>Nicht jedes Problem war eine gro\u00dfe Geschichte, aber es gab einige davon:<\/p>\n<p><strong>SSH-Key-Rechte unter Windows.<\/strong> Mein privater Schl\u00fcssel lag in einem OneDrive-Ordner mit zu offenen Zugriffsrechten:<\/p>\n<pre><code class=\"language-bash\" data-line=\"\">UNPROTECTED PRIVATE KEY FILE!\nPermissions for &#039;studyswipe-key.pem&#039; are too open.\n<\/code><\/pre>\n<p>Gel\u00f6st mit <strong>icacls<\/strong>, um die Rechte auf meinen eigenen Benutzer zu beschr\u00e4nken. Ein Windows-spezifisches Problem, das man auf einem Linux-Rechner so nie h\u00e4tte.<\/p>\n<p><strong>Java-Versions-Chaos.<\/strong> Mein lokal installiertes Java war neuer als das, was der im Projekt hinterlegte Gradle-Wrapper verstand:<\/p>\n<pre><code class=\"language-bash\" data-line=\"\">Unsupported class file major version 69\n<\/code><\/pre>\n<p>Erschwerend kam dazu, dass <strong>java -version<\/strong> im Terminal eine andere Version anzeigte als die, mit der Gradle tats\u00e4chlich arbeitete. Auf meinem Rechner waren mehrere Java-Versionen gleichzeitig installiert, und je nachdem welches Werkzeug man fragte, bekam man eine andere Antwort.<\/p>\n<p><strong>Prozesse, die an der SSH-Sitzung h\u00e4ngen.<\/strong> Als ich das Backend testweise direkt gestartet hatte, war es nach einem Verbindungsabbruch pl\u00f6tzlich weg. Ein Programm, das man im Vordergrund einer SSH-Sitzung startet, wird beendet, sobald die Verbindung endet, auch unbeabsichtigt. Die L\u00f6sung war, es als systemd-Dienst einzurichten, der unabh\u00e4ngig von der Sitzung l\u00e4uft und nach einem Neustart automatisch wieder hochkommt.<\/p>\n<p><strong>Die alte GitLab-Pipeline.<\/strong> Sie war seit der Projektabgabe nicht mehr gelaufen und schlug fehl, weil in der <strong>settings.gradle<\/strong> ein Modul eingebunden war, dessen Ordner es gar nicht mehr gab. Lokal mit \u00e4lterem Gradle war das nur eine Warnung, mit der neueren Version im CI-Image ein harter Fehler. Zus\u00e4tzlich hab ich die Gradle-Version im Image festgenagelt, statt sie \u00fcber <strong>latest<\/strong> bestimmen zu lassen.<\/p>\n<h2>Ein Fehler nachdem man dachte alles sei getan<\/h2>\n<p>Dieses Problem ist mir nach meiner Pr\u00e4sentation passiert. Als ich das Deployment eigentlich schon f\u00fcr fertig hielt ist beim erneuten Ausf\u00fchren von <strong>deploy.ps1<\/strong> der ECR-Login fehlgeschlagen:<\/p>\n<pre><code class=\"language-powershell\" data-line=\"\">Error response from daemon: login attempt to\nhttps:\/\/...amazonaws.com\/v2\/ failed with status: 400 Bad Request\n<\/code><\/pre>\n<p>Was folgte, war eine Reihe von Theorien auf der Suche nach dem Fehler:<\/p>\n<p><strong>Erste Theorie: Der Token ist abgelaufen.<\/strong> Klingt naheliegend, weil zwischen den Versuchen lag etwas Zeit. Stimmt aber nicht, denn der Token wird bei jedem Aufruf neu bei AWS angefordert, ein \u201calter\u201d Token existiert also gar nicht.<\/p>\n<p><strong>Zweite Theorie: Die Uhrzeit meiner Docker-VM stimmt nicht.<\/strong> Docker Desktop l\u00e4uft unter Windows \u00fcber eine Linux-VM, die eine eigene Systemuhr hat. Und tats\u00e4chlich zeigte <strong>docker ps<\/strong> einen Container mit dem Erstellungsdatum \u201c2 years ago\u201d, den es unm\u00f6glich so lange geben konnte. AWS reagiert empfindlich auf falsche Systemzeiten. Nach einem Neustart der VM war der Zeitstempel korrekt, aber der Login-Fehler blieb.<\/p>\n<p><strong>Dritte Theorie: Es liegt an der Terminal-Umgebung.<\/strong> Ein manueller Test in einem separaten PowerShell-Fenster hatte funktioniert, im IDE-Terminal nicht. Also lag es vermutlich an unterschiedlichen Umgebungsvariablen. Auch das lie\u00df sich widerlegen, da in einem komplett frischen externen Fenster derselbe Fehler auftrat.<\/p>\n<p>Was am Ende geholfen hat, war ein direkter Vergleichstest mit <strong>demselben<\/strong> Token. Diese Variante schlug fehl:<\/p>\n<pre><code class=\"language-powershell\" data-line=\"\">aws ecr get-login-password --region $REGION `\n    | docker login --username AWS --password-stdin $REGISTRY\n<\/code><\/pre>\n<p>Diese funktionierte sofort:<\/p>\n<pre><code class=\"language-powershell\" data-line=\"\">$token = (aws ecr get-login-password --region $REGION).Trim()\ndocker login --username AWS `\n    --password $token `\n    $REGISTRY\n<\/code><\/pre>\n<p>Der Unterschied ist die Pipe. Windows-PowerShell reicht scheinbar Text zwischen zwei externen Programmen anders durch als innerhalb der eigenen Umgebung. Dabei findet vermutlich eine Umkodierung statt, die bei normalem Text nie auff\u00e4llt, bei einem langen, zuf\u00e4llig aussehenden Token aber offenbar zu Besch\u00e4digungen f\u00fchren kann. Zumindest w\u00e4re das eine Erkl\u00e4rung f\u00fcr mich. Der Token sah am Bildschirm identisch aus, die L\u00e4nge stimmte und trotzdem kam bei Docker etwas anderes an.<\/p>\n<p>Drei falsche Theorien und jedes mal die Kette weiter in einzelne Teile zerlegt und jeden Teil einzeln getestet bis man das Problem gefunden hat.<\/p>\n<h2>Der Smoke-Test<\/h2>\n<p>Eine Sache die ich noch unbedingt hinzuf\u00fcgen wollte war ein Smoke-Test. Da mein Deployment-Skript immer mit \u201cFertig\u201d endete, unabh\u00e4ngig davon, ob die App danach wirklich erreichbar war. <code class=\"\" data-line=\"\">terraform apply<\/code> sagt nur, dass die Ressourcen existieren und nicht, dass etwas funktioniert.<\/p>\n<p>Also hab ich am Ende einen Test erg\u00e4nzt, der wiederholt versucht, die Seite aufzurufen:<\/p>\n<pre><code class=\"language-powershell\" data-line=\"\">for ($i = 1; $i -le $maxAttempts; $i++) {\n  try {\n    $r = Invoke-WebRequest -Uri &quot;http:\/\/$ip&quot; `\n        -UseBasicParsing -TimeoutSec 5\n    if ($r.StatusCode -eq 200) {\n      $success = $true\n      break\n    }\n  } catch {\n    Write-Host &quot;... noch nicht bereit ($i von $maxAttempts)&quot;\n  }\n  Start-Sleep -Seconds 10\n}\n<\/code><\/pre>\n<p>Ein echter Durchlauf sieht dann so aus:<\/p>\n<pre><code class=\"\" data-line=\"\">=== 5. Smoke-Test ===\n... noch nicht bereit (Versuch 1 von 18)\n... noch nicht bereit (Versuch 2 von 18)\n...\nApp erreichbar nach 120 Sekunden (Status 200)\n<\/code><\/pre>\n<p>Zwei Minuten von einer leeren AWS-Umgebung bis zur laufenden Anwendung und jetzt automatisch best\u00e4tigt statt manuell im Browser nachgeschaut.<\/p>\n<p>Beim ersten echten Einsatz hat der Test \u00fcbrigens sofort etwas gefunden: Er meldete, dass die App nicht antwortet, weil die Images gar nicht erst in ECR angekommen waren. Ohne den Test h\u00e4tte ich das erst gemerkt, wenn ich zuf\u00e4llig die Seite aufgerufen h\u00e4tte.<\/p>\n<h2>Was am Ende steht<\/h2>\n<p>Ein Skript, das aus einer leeren AWS-Umgebung heraus die komplette Infrastruktur aufbaut, die Anwendung baut und ausliefert und am Ende \u00fcberpr\u00fcft, ob sie wirklich l\u00e4uft. Dazu drei weitere Skripte f\u00fcr den restlichen Lebenszyklus: destroy, pause, resume.<\/p>\n<p>Der Unterschied zwischen \u201cpause\u201d und \u201cdestroy\u201d ist das <strong>destroy<\/strong> und <strong>deploy<\/strong> beweisen, dass wirklich alles reproduzierbar aus dem Code entsteht, allerdings alle Daten daf\u00fcr ins Wasser fallen, weil eine neue Instanz mit neuer Festplatte entsteht. <strong>pause<\/strong> und <strong>resume<\/strong> erhalten die Daten, beweisen aber nichts \u00fcber den Neuaufbau. Am Ende brauchte ich beide, da ich unbedingt eine Option wollte, die Instanz runterzufahren oder zu pausieren ohne dabei zum Beispiel Karteikarten zu l\u00f6schen. Das w\u00fcrde den ganzen Sinn des StudySwipe Projekts besiegen.<\/p>\n<p>Damit <strong>pause<\/strong> und <strong>resume<\/strong> \u00fcberhaupt praktikabel sind, kam noch eine Elastic IP dazu, eine feste \u00f6ffentliche Adresse, die an die Instanz gebunden bleibt. Ohne sie bekommt eine gestoppte Instanz beim n\u00e4chsten Start eine v\u00f6llig neue IP, was jedes Lesezeichen und jeden SSH-Befehl ung\u00fcltig macht. In Terraform sind das drei Zeilen. Der eigentliche Punkt ist, dass die Zuordnung damit automatisch mitwandert, statt ein manueller Schritt zu sein, den man vergessen kann.<\/p>\n<h2>Was als N\u00e4chstes m\u00f6glich w\u00e4re<\/h2>\n<p><strong>HTTPS.<\/strong> Aktuell fehlt es, und konkret merkt man das an einer Stelle: Die App setzt beim Ordnerwechsel ein <strong>secure<\/strong>-Cookie, das Browser \u00fcber eine unverschl\u00fcsselte Verbindung grunds\u00e4tzlich ablehnen. Dadurch funktioniert die Lernkarten-Ansicht nicht. Um das sauber zu l\u00f6sen, braucht es eine Kette von Voraussetzungen: feste IP (habe ich inzwischen \u00fcber eine Elastic IP), dann eine Domain, auf die ein Zertifikat ausgestellt werden kann.<\/p>\n<p><strong>Datenbank als eigener Dienst.<\/strong> Postgres l\u00e4uft aktuell als Container auf derselben Instanz. Mit RDS w\u00e4re die Datenbank unabh\u00e4ngig von der Instanz, Daten w\u00fcrden dann auch ein <strong>destroy<\/strong> \u00fcberleben.<\/p>\n<p><strong>Die Pipeline weiter nach vorne ziehen.<\/strong> Aktuell baue und pushe ich die Images von meinem eigenen Rechner aus. Der n\u00e4chste logische Schritt w\u00e4re, das in die GitLab-CI zu verlagern, sodass ein Push auf den Branch den ganzen Ablauf ausl\u00f6st.<\/p>\n<h2>Learnings<\/h2>\n<p><strong><code class=\"\" data-line=\"\">latest<\/code> ist kein fester Punkt.<\/strong> Zweimal ist mir die Umgebung unter den F\u00fc\u00dfen weggerutscht, ohne dass ich etwas ge\u00e4ndert hatte, einmal bei Postgres, einmal beim Gradle-Image in der CI. Seitdem pinne ich Versionen, sobald Reproduzierbarkeit eine Rolle spielt.<\/p>\n<p><strong>Infrastruktur und Anwendung sind unterschiedliche Ebenen mit unterschiedlichen Lebenszyklen.<\/strong> Das ECR-Timing-Problem war im Kern kein Timing-Problem, sondern ein Struktur-Problem: Etwas, das sich fast nie \u00e4ndert (die Registry), lag im selben Zustand wie etwas, das bei jedem Test neu gebaut wird (die Instanz). Die Trennung hat nicht das Symptom behandelt, sondern die Ursache entfernt.<\/p>\n<p><strong>Eine L\u00f6sung, die funktioniert, ist nicht automatisch die richtige.<\/strong> Das schnelle Nachpushen beim ECR-Problem hat funktioniert. Trotzdem war es keine L\u00f6sung, sondern eine Verlagerung des Problems auf mein Gl\u00fcck mit der Upload-Geschwindigkeit. Der Moment, in dem mir das aufgefallen ist, war r\u00fcckblickend wertvoller als der Fix selbst.<\/p>\n<p><strong>Erst verstehen, dann automatisieren.<\/strong> Ich hab jeden Schritt zuerst von Hand gemacht, die App manuell auf der Instanz zum Laufen gebracht, die Container-Struktur manuell getestet, und erst danach automatisiert. Das hat sich mehrfach ausgezahlt: Wenn im automatisierten Ablauf etwas schiefging, wusste ich, wie das Ergebnis aussehen sollte, und konnte den Fehler eingrenzen, statt bei null anzufangen.<\/p>\n<p><strong>Wenn eine Theorie nicht passt, weiter zerlegen statt weiter raten.<\/strong> Bei der Docker-Login-Geschichte hatte ich drei plausible Erkl\u00e4rungen, die alle falsch waren. Weitergeholfen hat erst, die Befehlskette in ihre Einzelteile zu trennen und mit demselben Token beide Varianten direkt gegeneinander zu testen. Das ist wahrscheinlich mein pers\u00f6nlich wichtigstes Learning aus dem Projekt, nicht ein technisches Detail, sondern eine Methode.<\/p>\n<h2>Links<\/h2>\n<ul>\n<li><a href=\"https:\/\/developer.hashicorp.com\/terraform\/docs\">Terraform-Dokumentation<\/a><\/li>\n<li><a href=\"https:\/\/www.youtube.com\/watch?v=rsct-JvJmKs\">Youtube Video: Using Terraform with AWS<\/a><\/li>\n<li><a href=\"https:\/\/docs.aws.amazon.com\/AmazonECR\/latest\/userguide\/getting-started-cli.html\">AWS ECR \u2013 Getting Started<\/a><\/li>\n<li><a href=\"https:\/\/docs.spring.io\/spring-boot\/gradle-plugin\/packaging.html\">Spring Boot Gradle Plugin \u2013 Packaging<\/a><\/li>\n<li><a href=\"https:\/\/docs.docker.com\/compose\/\">Docker Compose Dokumentation<\/a><\/li>\n<\/ul>\n<\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wie aus einem Karteikarten-Projekt, das bisher nur lokal lief, ein automatisiertes AWS-Deployment wurde: Terraform, Docker und ECR, ein Skript f\u00fcr alles. Und die Stolpersteine, die dabei n\u00f6tig waren.<\/p>\n","protected":false},"author":1334,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false,"_ppma_block_editor_authors":""},"categories":[1],"tags":[84,150,7,3,1264,514],"ppma_author":[1263],"class_list":["post-29224","post","type-post","status-publish","format-standard","hentry","category-allgemein","tag-aws","tag-ci-cd","tag-cloud","tag-docker","tag-studyswipe","tag-terraform"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"Wie aus einem Karteikarten-Projekt, das bisher nur lokal lief, ein automatisiertes AWS-Deployment wurde: Terraform, Docker und ECR, ein Skript f\u00fcr alles. Und die Stolpersteine, die dabei n\u00f6tig waren.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Kai M\u00fcller\"\/>\n\t<meta name=\"keywords\" content=\"aws,ci\/cd,cloud,docker,studyswipe,terraform\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Computer Science Blog\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"StudySwipe in AWS | Computer Science Blog @ HdM Stuttgart\" \/>\n\t\t<meta property=\"og:description\" content=\"Wie aus einem Karteikarten-Projekt, das bisher nur lokal lief, ein automatisiertes AWS-Deployment wurde: Terraform, Docker und ECR, ein Skript f\u00fcr alles. Und die Stolpersteine, die dabei n\u00f6tig waren.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-09-12T06:00:00+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-09-11T19:47:47+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"StudySwipe in AWS | Computer Science Blog @ HdM Stuttgart\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Wie aus einem Karteikarten-Projekt, das bisher nur lokal lief, ein automatisiertes AWS-Deployment wurde: Terraform, Docker und ECR, ein Skript f\u00fcr alles. Und die Stolpersteine, die dabei n\u00f6tig waren.\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/2026\\\/09\\\/12\\\/studyswipe-in-aws\\\/#article\",\"name\":\"StudySwipe in AWS | Computer Science Blog @ HdM Stuttgart\",\"headline\":\"StudySwipe in AWS\",\"author\":{\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/author\\\/kai_mller\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/#organization\"},\"datePublished\":\"2026-09-12T08:00:00+02:00\",\"dateModified\":\"2026-09-11T21:47:47+02:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/2026\\\/09\\\/12\\\/studyswipe-in-aws\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/2026\\\/09\\\/12\\\/studyswipe-in-aws\\\/#webpage\"},\"articleSection\":\"Allgemein, AWS, CI\\\/CD, Cloud, Docker, StudySwipe, Terraform, Kai M\\u00fcller\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/2026\\\/09\\\/12\\\/studyswipe-in-aws\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/category\\\/allgemein\\\/#listItem\",\"name\":\"Allgemein\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/category\\\/allgemein\\\/#listItem\",\"position\":2,\"name\":\"Allgemein\",\"item\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/category\\\/allgemein\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/2026\\\/09\\\/12\\\/studyswipe-in-aws\\\/#listItem\",\"name\":\"StudySwipe in AWS\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/2026\\\/09\\\/12\\\/studyswipe-in-aws\\\/#listItem\",\"position\":3,\"name\":\"StudySwipe in AWS\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/category\\\/allgemein\\\/#listItem\",\"name\":\"Allgemein\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/#organization\",\"name\":\"Computer Science Blog @ HdM Stuttgart\",\"description\":\"on computer science and media topics\",\"url\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/author\\\/kai_mller\\\/#author\",\"url\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/author\\\/kai_mller\\\/\",\"name\":\"Kai M\\u00fcller\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/2026\\\/09\\\/12\\\/studyswipe-in-aws\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/89fae5626225f22c9833db4b7af9976d59d51f59135af4dcf5c82a60cc0966b4?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Kai M\\u00fcller\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/2026\\\/09\\\/12\\\/studyswipe-in-aws\\\/#webpage\",\"url\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/2026\\\/09\\\/12\\\/studyswipe-in-aws\\\/\",\"name\":\"StudySwipe in AWS | Computer Science Blog @ HdM Stuttgart\",\"description\":\"Wie aus einem Karteikarten-Projekt, das bisher nur lokal lief, ein automatisiertes AWS-Deployment wurde: Terraform, Docker und ECR, ein Skript f\\u00fcr alles. Und die Stolpersteine, die dabei n\\u00f6tig waren.\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/2026\\\/09\\\/12\\\/studyswipe-in-aws\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/author\\\/kai_mller\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/index.php\\\/author\\\/kai_mller\\\/#author\"},\"datePublished\":\"2026-09-12T08:00:00+02:00\",\"dateModified\":\"2026-09-11T21:47:47+02:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/#website\",\"url\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/\",\"name\":\"Computer Science Blog @ HdM Stuttgart\",\"description\":\"on computer science and media topics\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/blog.mi.hdm-stuttgart.de\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"StudySwipe in AWS | Computer Science Blog @ HdM Stuttgart","description":"Wie aus einem Karteikarten-Projekt, das bisher nur lokal lief, ein automatisiertes AWS-Deployment wurde: Terraform, Docker und ECR, ein Skript f\u00fcr alles. Und die Stolpersteine, die dabei n\u00f6tig waren.","canonical_url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/","robots":"max-image-preview:large","keywords":"aws,ci\/cd,cloud,docker,studyswipe,terraform","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/#article","name":"StudySwipe in AWS | Computer Science Blog @ HdM Stuttgart","headline":"StudySwipe in AWS","author":{"@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/author\/kai_mller\/#author"},"publisher":{"@id":"https:\/\/blog.mi.hdm-stuttgart.de\/#organization"},"datePublished":"2026-09-12T08:00:00+02:00","dateModified":"2026-09-11T21:47:47+02:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/#webpage"},"isPartOf":{"@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/#webpage"},"articleSection":"Allgemein, AWS, CI\/CD, Cloud, Docker, StudySwipe, Terraform, Kai M\u00fcller"},{"@type":"BreadcrumbList","@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/blog.mi.hdm-stuttgart.de#listItem","position":1,"name":"Home","item":"https:\/\/blog.mi.hdm-stuttgart.de","nextItem":{"@type":"ListItem","@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/#listItem","name":"Allgemein"}},{"@type":"ListItem","@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/#listItem","position":2,"name":"Allgemein","item":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/","nextItem":{"@type":"ListItem","@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/#listItem","name":"StudySwipe in AWS"},"previousItem":{"@type":"ListItem","@id":"https:\/\/blog.mi.hdm-stuttgart.de#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/#listItem","position":3,"name":"StudySwipe in AWS","previousItem":{"@type":"ListItem","@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/#listItem","name":"Allgemein"}}]},{"@type":"Organization","@id":"https:\/\/blog.mi.hdm-stuttgart.de\/#organization","name":"Computer Science Blog @ HdM Stuttgart","description":"on computer science and media topics","url":"https:\/\/blog.mi.hdm-stuttgart.de\/"},{"@type":"Person","@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/author\/kai_mller\/#author","url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/author\/kai_mller\/","name":"Kai M\u00fcller","image":{"@type":"ImageObject","@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/89fae5626225f22c9833db4b7af9976d59d51f59135af4dcf5c82a60cc0966b4?s=96&d=mm&r=g","width":96,"height":96,"caption":"Kai M\u00fcller"}},{"@type":"WebPage","@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/#webpage","url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/","name":"StudySwipe in AWS | Computer Science Blog @ HdM Stuttgart","description":"Wie aus einem Karteikarten-Projekt, das bisher nur lokal lief, ein automatisiertes AWS-Deployment wurde: Terraform, Docker und ECR, ein Skript f\u00fcr alles. Und die Stolpersteine, die dabei n\u00f6tig waren.","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/blog.mi.hdm-stuttgart.de\/#website"},"breadcrumb":{"@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/#breadcrumblist"},"author":{"@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/author\/kai_mller\/#author"},"creator":{"@id":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/author\/kai_mller\/#author"},"datePublished":"2026-09-12T08:00:00+02:00","dateModified":"2026-09-11T21:47:47+02:00"},{"@type":"WebSite","@id":"https:\/\/blog.mi.hdm-stuttgart.de\/#website","url":"https:\/\/blog.mi.hdm-stuttgart.de\/","name":"Computer Science Blog @ HdM Stuttgart","description":"on computer science and media topics","inLanguage":"en-US","publisher":{"@id":"https:\/\/blog.mi.hdm-stuttgart.de\/#organization"}}]},"og:locale":"en_US","og:site_name":"Computer Science Blog","og:type":"article","og:title":"StudySwipe in AWS | Computer Science Blog @ HdM Stuttgart","og:description":"Wie aus einem Karteikarten-Projekt, das bisher nur lokal lief, ein automatisiertes AWS-Deployment wurde: Terraform, Docker und ECR, ein Skript f\u00fcr alles. Und die Stolpersteine, die dabei n\u00f6tig waren.","og:url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/","article:published_time":"2026-09-12T06:00:00+00:00","article:modified_time":"2026-09-11T19:47:47+00:00","twitter:card":"summary","twitter:title":"StudySwipe in AWS | Computer Science Blog @ HdM Stuttgart","twitter:description":"Wie aus einem Karteikarten-Projekt, das bisher nur lokal lief, ein automatisiertes AWS-Deployment wurde: Terraform, Docker und ECR, ein Skript f\u00fcr alles. Und die Stolpersteine, die dabei n\u00f6tig waren."},"aioseo_meta_data":{"post_id":"29224","title":null,"description":null,"keywords":null,"keyphrases":{"focus":{"keyphrase":"","score":0,"analysis":{"keyphraseInTitle":{"score":0,"maxScore":9,"error":1}}},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"created":"2026-09-08 09:40:10","updated":"2026-09-10 21:03:45","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/blog.mi.hdm-stuttgart.de\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/\" title=\"Allgemein\">Allgemein<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tStudySwipe in AWS\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/blog.mi.hdm-stuttgart.de"},{"label":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},{"label":"StudySwipe in AWS","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/09\/12\/studyswipe-in-aws\/"}],"jetpack-related-posts":[{"id":26208,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/02\/29\/die-meere-der-systemtechnik-navigieren-eine-reise-durch-die-bereitstellung-einer-aktien-webanwendung-in-der-cloud\/","url_meta":{"origin":29224,"position":0},"title":"Die Meere der Systemtechnik navigieren: Eine Reise durch die Bereitstellung einer Aktien-Webanwendung in der Cloud","author":"mk306","date":"29. February 2024","format":false,"excerpt":"Auf zu neuen Ufern: Einleitung Die Cloud-Computing-Technologie hat die Art und Weise, wie Unternehmen Anwendungen entwickeln, bereitstellen und skalieren, revolutioniert. In diesem Beitrag, der im Rahmen der Vorlesung \u201c143101a System Engineering und Management\u201d entstanden ist, werden wir uns darauf konzentrieren, wie eine bereits bestehende Webanwendung zur Visualisierung und Filterung von\u2026","rel":"","context":"In &quot;Cloud Technologies&quot;","block_context":{"text":"Cloud Technologies","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/cloud-technologies\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/Dashboard2-Kopie-1.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/Dashboard2-Kopie-1.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/Dashboard2-Kopie-1.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/Dashboard2-Kopie-1.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/Dashboard2-Kopie-1.png?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/Dashboard2-Kopie-1.png?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":12032,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2020\/09\/30\/admin-panel-web-app-in-der-aws-cloud\/","url_meta":{"origin":29224,"position":1},"title":"Admin Panel (Web App) in der AWS Cloud","author":"ss447","date":"30. September 2020","format":false,"excerpt":"1. Einleitung Im Rahmen der Vorlesung \u201eSoftware Development for Cloud Computing\u201c haben wir uns als Gruppe dazu entschieden aufbauend auf teilweise bereits vorhandener Codebasis an einem Startup-Projekt weiterzuarbeiten. Der Hauptfokus lag bei uns auf dem Ausbau von DevOps-Aspekten und auf dem eines stabilen und sicheren Systems, welches auch in der\u2026","rel":"","context":"In &quot;Cloud Technologies&quot;","block_context":{"text":"Cloud Technologies","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/cloud-technologies\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/img.youtube.com\/vi\/qw9ZkWnvR4M\/0.jpg?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":21163,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/09\/15\/cloud-basierter-password-manager\/","url_meta":{"origin":29224,"position":2},"title":"Cloud basierter Password Manager","author":"bs103","date":"15. September 2021","format":false,"excerpt":"von Benjamin Schweizer (bs103) und Max Eichinger (me110) Abstract K\u00f6nnen Passwort Manager Anbieter meine Passw\u00f6rter lesen? Wir wollten auf Nummer sichergehen und haben unseren Eigenen entwickelt. Dieser Artikel zeigt auf welche Schritte wir hierf\u00fcr unternehmen mussten.Dabei haben wir unser Frontend mittels Flutter und unser Backend in AWS umgesetzt. Au\u00dferdem gehen\u2026","rel":"","context":"In &quot;Cloud Technologies&quot;","block_context":{"text":"Cloud Technologies","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/cloud-technologies\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/09\/image0-4-150x150.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/09\/image0-4-150x150.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/09\/image0-4-150x150.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/09\/image0-4-150x150.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/09\/image0-4-150x150.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":28550,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/02\/21\/entwicklung-einer-verteilten-cloud-anwendung-am-beispiel-eines-multiplayer-spiels\/","url_meta":{"origin":29224,"position":3},"title":"Entwicklung einer verteilten Cloud-Anwendung am Beispiel eines Multiplayer Spiels","author":"Tom Bestvater","date":"21. February 2026","format":false,"excerpt":"Einleitung Den meisten sollte das Spielprinzip von \u201cCookie Clicker\u201d bekannt sein: Ein Klick auf einen Keks erh\u00f6ht den Spielstand um einen Punkt. Das Spiel ist endlos, hat keine Punktegrenze. Es geht darum, im Leaderboard nach oben zu klettern. Im Rahmen der Vorlesung \u201cSystem Engineering and Management\u201d (143101a)\u00a0 erweiterten wir das\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2026\/02\/Auth-Flow-Diagram-1.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2026\/02\/Auth-Flow-Diagram-1.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2026\/02\/Auth-Flow-Diagram-1.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2026\/02\/Auth-Flow-Diagram-1.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":28084,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2025\/09\/14\/springboot-zu-serverless-probleme-und-paradigmen\/","url_meta":{"origin":29224,"position":4},"title":"Springboot zu Serverless: Probleme und Paradigmen","author":"Julian Schniepp","date":"14. September 2025","format":false,"excerpt":"Im Rahmen der Vorlesung \u201eSoftware Development for Cloud\u00a0Computing\u201c sollte jedes Team ein eigenes Cloud\u2011Projekt umsetzen. Unser Projekt Taskflow sollte dabei ein serverloses Todo\u2011Management\u2011System werden. Ziel war es dabei, neue und vor allem industrierelevante Cloud\u2011Technologien praktisch zu erlernen. Der Backend\u2011Teil ist mit Spring\u2011Boot realisiert, welcher \u00fcber AWS\u00a0Lambda und API\u00a0Gateway bereitgestellt modular\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":23679,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/08\/31\/jobsuche-portal\/","url_meta":{"origin":29224,"position":5},"title":"Jobsuche Portal","author":"ag164","date":"31. August 2022","format":false,"excerpt":"SS22 - Dev4Cloud Projekt - von Robin H\u00e4rle und Anton Gerdts Ideenfindung \u00a0\u00a0\u00a0 Zu Beginn der Ideenfindungsphase f\u00fcr unser Projekt sahen wir uns die verschiedenen Apis auf Bund.dev an, um uns von der Thematik der verf\u00fcgbaren Daten inspirieren zu lassen. Wir entschieden uns ohne lange abzuw\u00e4gen daf\u00fcr ein Jobsuche-Portal mit\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_sharing_enabled":true,"authors":[{"term_id":1263,"user_id":1334,"is_guest":0,"slug":"kai_mller","display_name":"Kai M\u00fcller","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/89fae5626225f22c9833db4b7af9976d59d51f59135af4dcf5c82a60cc0966b4?s=96&d=mm&r=g","author_category":"","user_url":"","last_name":"M\u00fcller","first_name":"Kai","job_title":"","description":""}],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/29224","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/users\/1334"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/comments?post=29224"}],"version-history":[{"count":32,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/29224\/revisions"}],"predecessor-version":[{"id":29304,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/29224\/revisions\/29304"}],"wp:attachment":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/media?parent=29224"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/categories?post=29224"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/tags?post=29224"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/ppma_author?post=29224"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}