Skip to main content

3 posts tagged with "Docker"

Containerising .NET applications, resource limits and how images differ from bare metal.

View All Tags

ONNX Runtime Dies on Alpine: the ld-linux-x86-64.so.2 Error and Why libc6-compat Won't Save You

· 14 min read
Nguyễn Huỳnh Minh Tiến
Middle Fullstack Developer @ Utop.vn
Summary

onnxruntime-node ships prebuilt binaries built against glibc, and libonnxruntime.so.1 carries a DT_NEEDED entry pointing directly at ld-linux-x86-64.so.2 — glibc's own dynamic loader. Alpine uses musl, where that file does not exist, so dlopen fails at the library-loading step. Installing libc6-compat or gcompat does not fix it: they provide the loader file so you clear the first error, then die on the second one, __vsnprintf_chk: symbol not found. The practical answer is to change the base image to node:22-bookworm-slim, paying about 89 MB more for something that actually runs.

At 21:30 I added a Dockerfile for ClubDay — an event web app whose drawing game is scored by an ONNX model running on the server itself. The base image was reflex: node:22-alpine, because it is small.

The image built cleanly. The container came up. docker ps was green. Then, at the moment the server loaded the model, everything collapsed with an error line that never mentions ONNX:

Error loading shared library ld-linux-x86-64.so.2: No such file or directory
(needed by /app/node_modules/onnxruntime-node/bin/napi-v6/linux/x64/libonnxruntime.so.1)

At 21:44 I changed one line in the Dockerfile and everything worked. But the fourteen minutes in between are worth writing down, because that error line names a file you never installed, never declared in package.json, and which is simply always there on your development machine.

Traefik + Cloudflare: Why Your Certificates All Expire on Day 60

· 19 min read
Nguyễn Huỳnh Minh Tiến
Middle Fullstack Developer @ Utop.vn
Summary

When a domain is proxied through Cloudflare (the orange cloud) with SSL mode Full (strict), ACME HTTP-01 cannot work: Let's Encrypt requests http://domain/.well-known/acme-challenge/... on port 80, Cloudflare receives it and calls back to your origin over HTTPS:443 — where Traefik's challenge handler does not live. You need a certificate to get through Cloudflare, and you need to get through Cloudflare to obtain a certificate. You can work around the first issuance by temporarily switching the cloud to grey, but the automatic renewal around day 60 will fail silently and every site will expire at the same time. The way out is DNS-01: validation through a TXT record via the Cloudflare API, with no request ever touching the origin. The price is that certificates can only be issued for zones your API token can see.

This VPS runs 14 containers behind one shared reverse proxy: 10 WordPress sites, a .NET API, an Angular app, an event web app, and Traefik itself. Thirteen hostnames, all behind Cloudflare, all needing HTTPS, and nobody wanting to renew a certificate by hand.

The architecture is boring in the best way. What is interesting sits between Cloudflare and Let's Encrypt — two systems each doing exactly their job, which together produce a circular dependency you only discover on the day your certificates expire. That is, two months after everything appeared to be finished.

Why Does .NET Eat 96% of Container RAM While Idle? Server GC and How to Cut Memory by 65%

· 12 min read
Nguyễn Huỳnh Minh Tiến
Middle Fullstack Developer @ Utop.vn
Summary

An ASP.NET Core 9 container sat at 987 MiB against a 1 GiB ceiling (96%) after two hours without receiving a single request. This was not a memory leak: the number was absolutely flat across repeated measurements, which is the signature of retained heap rather than a leak. The cause was Server GC — the ASP.NET Core default, which creates one heap per CPU core and releases very little memory back to the operating system. Setting DOTNET_gcServer=0 and DOTNET_GCConserveMemory=5 brought anonymous memory down from 822 MB to 280 MB (−65%), measured as stable after 30 minutes. The trade-off: Workstation GC gives lower throughput under heavy load on many-core machines.

This post records a real diagnostic session on a 4-core / 8 GB VPS running nine WordPress sites alongside a .NET API. Every number below is measured, not illustrative.