Startseite / Artikel

Behebung des Fehlers „Libssl.so.1.1 fehlt“ in Prisma unter Alpine Docker

Prismas Abfragemotor stürzt in Alpine-Docker-Images ab, wenn libssl.so.1.1 fehlt. Was geht eigentlich mit OpenSSL vor, und wie kann man das in Node.js endgültig beheben?

Verlag: REACTAPP.TOP6 Min. Lesezeit1479 Wörter

Quellen: Prisma Docker Error: `libssl.so.1.1` Not Found on Alpine

Sie haben den Dockerfile geschrieben, den Build gestartet und alles schien in Ordnung zu sein. Dann haben Sie docker compose up ausgeführt, den Log-Stream verfolgt – und genau in dem Moment, in dem die Anwendung mit der Datenbank verbunden werden sollte, ist sie abgestürzt.

Symptome

Falls eines dieser Symptome bekannt vorkommt, finden Sie unten die Erklärung sowie die Lösung:

  • Ein Absturzbericht, der angibt, dass eine gemeinsame Bibliothek mit dem Namen libssl.so.1.1 im gesamten Dateisystem nicht gefunden werden konnte
  • Ein Startfehler von Prisma mit der Meldung PrismaClientInitializationError: Unable to require libquery_engine-linux-musl.so.node
  • Eine Meldung, dass Prisma nicht herausfinden konnte, welche Version von libssl/openssl verwendet wird
  • Der durch docker compose up gestartete Container wird fast sofort wieder beendet
  • Eine Build-Komplettierung ohne irgendwelche Probleme, doch der Container stirbt sofort, sobald er versucht, mit der Datenbank zu kommunizieren
  • Der vollständige Fehler sieht in der Regel so aus:

    Details: Error loading shared library libssl.so.1.1: No such file or directory
    (needed by /app/node_modules/.prisma/client/libquery_engine-linux-musl.so.node)
    

    Der Stack

    Das Szenario, in dem dieses Problem auftrat, beinhaltete eine auf TypeScript basierende Node.js/Express-API, Prisma als ORM-Schicht sowie PostgreSQL als Datenbank. Die Dockerfile basierte auf node:20-alpine – ein vernünftiger Standard, da Images auf Basis von Alpine erheblich leichter sind als ihre Debian-Äquivalente: schneller herunterzuladen, geringeres Angriffsrisiko und im Allgemeinen besser für Produktivumgebungen geeignet.

    Der Build selbst verlief ohne Probleme. Es handelte sich um eine mehrstufige Einrichtung: TypeScript wurde in dist/ kompiliert, und der Prisma Client wurde korrekt generiert. Während des Builds traten keine Fehler auf. Die Probleme zeigten sich erst, als docker compose up tatsächlich den Container startete.

    Was wirklich vor sich geht

    Alpine Linux ist absichtlich stark reduziert. Das ist gerade der Reiz – deshalb sind die entstehenden Images so klein. Doch durch diese Minimalisierung lässt Alpine viele Pakete aus, die Images auf Basis von Debian oder Ubuntu standardmäßig enthalten.

    Prismas Abfragemotor wird als vorkompiliertes Binärdatei bereitgestellt – eine .so (Shared Object)-Datei, die zusammen mit Ihrem Projekt enthalten ist und in diesem Fall einen Namen wie libquery_engine-linux-musl.so.node hat. Diese Binärdatei ist für die eigentliche Kommunikation mit Ihrer Datenbank verantwortlich und benötigt zur Laufzeit direkt OpenSSL.

    Wichtig ist hier, dass diese Engine-Binärdatei auf eine bestimmte Version von OpenSSL verlinkt ist, nicht auf OpenSSL im allgemeinen Sinne. Welche Version sie erwartet, hängt sowohl von Ihrer Prisma-Version als auch von Ihrer Alpine-Version ab:

    • Frühere Versionen des Prisma-Engines waren auf OpenSSL 1.1 verlinkt, das libssl.so.1.1 bereitstellt
    • Jüngere Prisma-Versionen sind auf OpenSSL 3 basiert, das libssl.so.3 bereitstellt
    • Alpine-Versionen bis 3.16 lieferten standardmäßig OpenSSL 1.1
  • Alpine 3.17 und neuer liefern stattdessen OpenSSL 3
  • Deshalb hängt der genaue Fehler, auf den Sie stoßen, davon ab, welche Prisma- und Alpine-Versionen Sie kombinieren. Lesen Sie den Fehlertext sorgfältig durch – er nennt die spezifische fehlende Shared Library, und libssl.so.1.1 im Gegensatz zu libssl.so.3 erfordert ein anderes Paket sowie eine andere Lösung.

    Der Grund, warum dies erst zur Laufzeit auftritt und nicht bereits bei der Kompilierung, liegt darin, dass prisma generate – das während der Kompilierungsphase ausgeführt wird – den Query-Engine-Binärdatei niemals tatsächlich ausführt. Es erzeugt lediglich den TypeScript-Client-Code. Die Engine-Binärdatei selbst wird erst dann aufgerufen, wenn die Anwendung gestartet wird und Prisma versucht, eine Datenbankverbindung herzustellen. Zu diesem Zeitpunkt befinden Sie sich bereits in einem laufenden Container mit einer inkompatiblen oder fehlenden OpenSSL-Bibliothek, und es gibt keine weiteren Warnungen mehr, bevor etwas schiefgeht.

    Die Lösung

    Die Lösung besteht darin, OpenSSL explizit über Alpines Paketmanager apk in Ihrem Dockerfile zu installieren. Welches Paket Sie benötigen, hängt vollständig davon ab, was die Fehlermeldung Ihnen mitteilt.

    Falls der Fehler libssl.so.1.1 (OpenSSL 1.1) erwähnt:

    In neueren Alpine-Versionen (3.17 und höher) ist OpenSSL 1.1 standardmäßig nicht mehr installiert, daher benötigen Sie das Kompatibilitätspaket:

    RUN apk add --no-cache openssl1.1-compat
    

    Falls der Fehler libssl.so.3 (OpenSSL 3) erwähnt oder Sie eine aktuelle Prisma-Version unter einer neueren Alpine-Version ausführen:

    RUN apk add --no-cache openssl
    

    Dadurch wird die von Alpines Paketrepository für diese Version bereitgestellte OpenSSL-Version heruntergeladen – bei Alpine 3.17+ handelt es sich dabei um OpenSSL 3.

    Die sicherste Langzeitstrategie besteht darin, Ihre Versionen absichtlich festzulegen. Korrigieren Sie Ihre Alpine-Version über den Base-Image-Tag und fixieren Sie Ihre Prisma-Version in package.json, damit die OpenSSL-Anforderung konstant bleibt und nicht unerwartet zwischen den Umgebungen wechselt. In Prismas eigenen Veröffentlichungshinweisen wird aufgelistet, gegen welche OpenSSL-Build jede Engine-Datei kompiliert wurde.

    Falls Ihre Einrichtung eine mehrstufige Kompilierung verwendet, müssen Sie diese Zeile in beiden Stufen hinzufügen, da jede von ihnen unabhängig auf Alpine läuft:

    # Stage 1: Builder
    FROM node:20-alpine AS builder
    RUN apk add --no-cache openssl
    
    WORKDIR /app
    COPY package*.json ./
    COPY prisma ./prisma
    RUN npm install
    COPY . .
    RUN npx prisma generate
    RUN npm run build
    
    # Stage 2: Production
    FROM node:20-alpine AS production
    RUN apk add --no-cache openssl
    
    WORKDIR /app
    ENV NODE_ENV=production
    
    COPY package*.json ./
    COPY prisma ./prisma
    RUN npm install --omit=dev
    COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
    COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma
    COPY --from=builder /app/dist ./dist
    
    EXPOSE 4000
    CMD ["node", "dist/server.js"]
    

    Der Flag --no-cache verhindert, dass apk eine lokale Kopie des Paketindex speichert, wodurch die Größe der endgültigen Image-Datei klein bleibt – genau wie es der ursprüngliche Grund für die Wahl von Alpine war.

    Warum nicht einfach auf node:slim wechseln?

    Das ist eine berechtigte Frage. node:20-slim basiert auf Debian und nicht auf Alpine, und Debian liefert OpenSSL standardmäßig mit, wodurch dieses spezielle Versagen meist nicht auftritt. Viele Teams wechseln einfach darauf um und machen weiter.

    Das Problem ist die Bildgröße. Ein typisches Node-Bild auf Basis von Alpine hat eine Größe von etwa 50 bis 70 MB, während das entsprechende slim-Version auf Debian bei rund 150 bis 200 MB liegt. Multipliziert man diesen Unterschied mit Dutzenden von Bereitstellungen, mehreren Diensten sowie wiederholten Downloads durch alle Teammitglieder, summieren sich die Mengen schnell. Alpine weist außerdem standardmäßig eine kleinere Angriffsfläche auf, was in sicherheitskritischen Umgebungen ein bedeutender Faktor ist.

    Das Installieren des fehlenden Pakets über apk add ist ein gängiger Ansatz, der direkt in Prismas Anleitungen dokumentiert ist und bereits in unzähligen Produktiv-Dockerfiles zu finden ist. Der Wechsel auf ein auf Debian basierendes Image, um Probleme mit der Versionenabstimmung zu vermeiden, ist eine völlig valide Wahl – doch es lohnt sich dennoch, zu verstehen, warum die Abweichung überhaupt auftritt, unabhängig davon, für welches Basisimage man sich entscheidet.

    Ein Hinweis zu mehrstufigen Builds

    Falls die beiden FROM-Zeilen sowie die Syntax --from=builder im vorherigen Dockerfile unvertraut erschienen, hier eine kurze Erklärung für dieses Muster.

    Ein Dockerfile mit einem einzigen Stadium, das TypeScript kompiliert, führt dazu, dass der TypeScript-Compiler, ts-node, alle devDependencies sowie Ihre Rohquelldateien direkt in das Produktionsimage aufgenommen werden. All das sollte dort eigentlich nicht vorhanden sein – es vergrößert das Image und erweitert die Angriffsfläche für potenzielle Sicherheitslücken.

    Eine mehrstufige Build-Strategie hält die Komponenten ordnungsgemäß getrennt: Das Build-Stadium enthält alles, was zur Kompilierung des Projekts benötigt wird, während das Produktionsstadium von einer sauberen Basis ausgeht und nur das mitnimmt, was zum Laufzeitbetrieb tatsächlich erforderlich ist – die kompilierten Dateien aus dem Verzeichnis dist/, nur für die Produktion notwendige Abhängigkeiten sowie der generierte Prisma Client. Alles Überflüssige aus dem Build-Stadium wird automatisch zurückgelassen, sobald Docker das endgültige Image erstellt.

    Das Ergebnis ist ein kompaktes, für die Produktion bereites Image, das nicht mit seiner eigenen Build-Toolkette belastet wird.

    Nach der Korrektur

    Sobald die richtige OpenSSL-Version installiert ist, sollte das Ausführen von docker compose up endlich den gewünschten Ergebnis liefern:

    ✅ Connected to PostgreSQL
    🚀 Server listening on port 4000
    

    Falls Ihre Prisma-Einrichtung über Docker Compose läuft, muss als Nächstes eine Migration auf die in einem Container laufende Datenbank durchgeführt werden, anstatt auf etwas, das lokal läuft. Kurz gesagt bedeutet das das Ausführen von docker compose exec app npx prisma migrate deploy, wodurch der Migrationsbefehl aus dem laufenden Anwendungscontainer ausgeführt wird – wobei DATABASE_URL auf Ihren von Compose verwalteten Postgres-Dienst verweist und nicht auf Ressourcen auf dem Host-Computer.

    Der OpenSSL-Fehler wirkt beim ersten Auftreten einschüchternd – es handelt sich um einen dichten Block an binären Ausgabeinhalten, der mit ENOENT endet. Doch die Ursache ist eng gefasst und spezifisch: Prismas Abfragemotor ist ein kompiliertes Binärprogramm, das für eine bestimmte OpenSSL-Version erstellt wurde, und Alpine garantiert nicht, dass diese Version tatsächlich vorhanden ist. Lesen Sie die Fehlermeldung sorgfältig, ermitteln Sie, welche libssl-Version benötigt wird, installieren Sie diese entsprechend und sichern Sie die Versionen ab, damit das Verhalten unabhängig vom Laufort des Containers vorhersehbar bleibt.