With the pkg-cache BuildKit mirror on, `FROM almalinux/10-base` was resolved
by tag through the cache, so a compromised cache could substitute the image.
Each job using the mirror now looks up the base tag's digest DIRECTLY at
registry-1.docker.io (.gitea/scripts/resolve-base-image.sh, HTTPS, HEAD on
the manifest, regex-validated) and builds FROM image@sha256:... via the
BASE_IMAGE build-arg, so the mirror can only serve content-addressed blobs.
If the lookup fails, the mirror is NOT used for that job and the tag is pulled
straight from Docker Hub. The digest is resolved every run (no manual bumps);
local builds default to the tag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The first Node 24/26 run timed out on every job: public AlmaLinux mirrors
served the runners at 1-150 kB/s. Use the CT130 package cache the same way
cloud-apache-container does (same pkg-cache-repos.sh and pinned CA), with
the upstream repos as fallback when the probe fails. epel-release is now
GPG-verified before install instead of fetched unverified by URL.
- npm 12 requires Node >=22.22.2; on 18/20 keep the npm Node ships with.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- One install-node.sh <major> replaces install-node{18,20,22}.sh. It fails the
build if NodeSource hands back a version below the newest security release
for the line (22.23.4 / 24.22.1 / 26.12.1: CVE-2026-111497 Brotli OOB write
and 9 others), so a lagging mirror can't ship a vulnerable image silently.
18 and 20 are EOL: they still build, with a warning.
- CI matrix adds 24 and 26; fail-fast off so one line can't cancel the rest.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base image moves from AlmaLinux 9 to 10, which forces two related changes:
EPEL repo URL bumps to the el10 release RPM, and tini is replaced with
dumb-init as PID 1 since tini is not packaged in EPEL 10. Both provide the
signal forwarding and zombie reaping the wedged-container fix relies on.
Nginx 1.26 in AlmaLinux 10 deprecates the `listen ... http2` parameter, so
the server block now uses the separate `http2 on;` directive.
Also fixes three issues that affected WebSocket apps (socket.io, ws):
- `Connection: upgrade` was hardcoded on every proxied request, including
ordinary HTTP. Now driven by a `map $http_upgrade $connection_upgrade`
so only genuine upgrade requests carry it.
- No explicit proxy read/send timeout meant idle WebSockets were cut at
nginx's 60s default. Set to 600s, which clears any sane heartbeat without
pinning connection slots (worker_connections is 512, two per client).
- `large_client_header_buffers 2 1k` returned 400 for any single header
line over 1KB, which session cookies and bearer tokens routinely exceed.
Raised to the nginx default of 4 8k; buffers are allocated on demand, so
this only costs memory for requests that need it.
Verified by rendering the generated config and running it under nginx with
a Node backend: WebSocket handshakes return 101 and reach the upstream
'upgrade' event, polling requests arrive with `Connection: close`, and a
3KB cookie returns 200 where the old buffer setting returned 400.
README gains a WebSocket Support section covering the PM2 cluster-mode
trap, the concurrency ceiling, and reconnects on memory restarts.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A 10s postgresql restart took down transcribe.shadowdao.com-01 for ~17h
because pm2 gave up after 5 fast retries, the entrypoint's trailing
tail -f kept PID 1 alive, and the healthcheck (wget --spider on nginx
port 80) succeeded on the 301-to-https redirect regardless of whether
Node was alive.
Three coordinated fixes to the cnoc image:
- HEALTHCHECK: replace the redirect-passing wget probe with TCP-level
checks on 127.0.0.1:3000 (Node) and :80 (nginx). Tenant-agnostic, no
/ping dependency — catches the exact incident scenario (port 3000
closed when pm2 exits).
- entrypoint.sh: exec pm2 via tini so it becomes PID 1. When pm2
exhausts max_restarts and exits, the container exits and the
unless-stopped restart policy brings it back. Logs are tailed in the
background with -F (logrotate-safe).
- Dockerfile: install tini from EPEL for proper signal forwarding and
zombie reaping of nginx/crond children that reparent to PID 1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Changed healthcheck from hitting Node.js directly on port 3000/ping to
going through Nginx on port 80, ensuring the entire stack is validated.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Changed 'yum groupinstall' to 'dnf group install -y' to match AlmaLinux 9
package manager and ensure non-interactive installation.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add automatic ecosystem.config.js generation from package.json
- Create app directory automatically if missing
- Copy simple-website example when app directory is empty
- Remove redundant default app files from configs/
- Add HAProxy support with proper real IP forwarding
- Configure nginx to trust proxy headers from private networks
- Simplify entrypoint logic - always use /home/$user/app
This makes the container more user-friendly by eliminating the need for
manual PM2 configuration and ensuring the server always has a working app.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove curl from package installation to avoid curl/curl-minimal conflict
- Replace curl with wget in health check and Node.js installation scripts
- wget is already installed and provides same functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
The nginx default.conf is generated dynamically by create-nginx-config.sh
script at runtime, not copied during build.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add Dockerfile with AlmaLinux 9 base, Nginx reverse proxy, and PM2
- Support Node.js versions 18, 20, 22 with automated installation
- Implement memory-optimized configuration (256MB minimum, 512MB recommended)
- Add Memcached session storage for development environments
- Create comprehensive documentation (README, USER-GUIDE, MEMORY-GUIDE, CLAUDE.md)
- Include example applications (simple website and REST API)
- Add Gitea CI/CD pipeline for automated multi-version builds
- Provide local development script with helper utilities
- Implement health monitoring, log rotation, and backup systems
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>