From eff27e359be9a2156b1d2a78a8e978edbd2221b2 Mon Sep 17 00:00:00 2001 From: rameen Date: Sun, 23 Aug 2026 16:16:14 +0330 Subject: [PATCH] Fix staging containers failing on Windows Docker builds Strip CRLF from entrypoint scripts in backend/frontend images, use Docker DNS in staging nginx config, and wait for healthy app services before nginx. Co-authored-by: Cursor --- .gitattributes | 2 ++ backend/Dockerfile | 3 ++- frontend/Dockerfile | 3 ++- infrastructure/STAGING-DEPLOY.md | 2 ++ infrastructure/docker-compose.registry.yml | 6 ++++-- infrastructure/docker-compose.staging.yml | 6 ++++-- infrastructure/nginx/http-only.conf | 19 ++++++++----------- 7 files changed, 24 insertions(+), 17 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6673000 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Shell scripts must use LF — CRLF breaks Alpine entrypoints ("No such file or directory"). +*.sh text eol=lf diff --git a/backend/Dockerfile b/backend/Dockerfile index c7f3ec0..f381da1 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -47,7 +47,8 @@ COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh -RUN chmod +x /usr/local/bin/docker-entrypoint.sh +# Windows git/build context may use CRLF; strip before chmod (fixes dumb-init "No such file or directory"). +RUN sed -i 's/\r$//' /usr/local/bin/docker-entrypoint.sh && chmod +x /usr/local/bin/docker-entrypoint.sh RUN mkdir -p /app/logs && \ chown -R dyolink:nodejs /app diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 8256390..1a89df2 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -45,7 +45,8 @@ COPY --from=builder --chown=dyolink:nodejs /app/.next/standalone ./ COPY --from=builder --chown=dyolink:nodejs /app/.next/static ./.next/static COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh -RUN chmod +x /usr/local/bin/docker-entrypoint.sh +# Windows git/build context may use CRLF; strip before chmod (fixes dumb-init "No such file or directory"). +RUN sed -i 's/\r$//' /usr/local/bin/docker-entrypoint.sh && chmod +x /usr/local/bin/docker-entrypoint.sh USER dyolink diff --git a/infrastructure/STAGING-DEPLOY.md b/infrastructure/STAGING-DEPLOY.md index 529d6f3..4e879c8 100644 --- a/infrastructure/STAGING-DEPLOY.md +++ b/infrastructure/STAGING-DEPLOY.md @@ -249,6 +249,8 @@ On the Windows host, from repo `infrastructure/`: | Port 8088 unreachable | Windows firewall rule; confirm nginx container is up | | Backend restart loop | JWT secrets still placeholder; fix `backend.staging.env` | | Backend DB auth error | `DATABASE_URL` password ≠ `POSTGRES_PASSWORD` | +| `dumb-init docker-entrypoint.sh: No such file or directory` | Windows CRLF in shell scripts — fixed in Dockerfiles (rebuild images). | +| `nginx: host not found in upstream "backend:3000"` | Backend/frontend not running yet — fix entrypoint/env first; nginx config uses Docker DNS resolver. | **Logs:** diff --git a/infrastructure/docker-compose.registry.yml b/infrastructure/docker-compose.registry.yml index acc2caf..0d9306f 100644 --- a/infrastructure/docker-compose.registry.yml +++ b/infrastructure/docker-compose.registry.yml @@ -85,8 +85,10 @@ services: image: nginx:alpine container_name: dyolink_nginx_staging depends_on: - - backend - - frontend + backend: + condition: service_healthy + frontend: + condition: service_healthy ports: - "${STAGING_HTTP_PORT:-8088}:80" volumes: diff --git a/infrastructure/docker-compose.staging.yml b/infrastructure/docker-compose.staging.yml index 81e7ef4..043bd2e 100644 --- a/infrastructure/docker-compose.staging.yml +++ b/infrastructure/docker-compose.staging.yml @@ -87,8 +87,10 @@ services: image: nginx:alpine container_name: dyolink_nginx_staging depends_on: - - backend - - frontend + backend: + condition: service_healthy + frontend: + condition: service_healthy ports: - "${STAGING_HTTP_PORT:-8088}:80" volumes: diff --git a/infrastructure/nginx/http-only.conf b/infrastructure/nginx/http-only.conf index 133c685..9594e0b 100644 --- a/infrastructure/nginx/http-only.conf +++ b/infrastructure/nginx/http-only.conf @@ -1,15 +1,10 @@ # HTTP only — local dev and IP-based staging (no TLS). # Use with: docker compose and map host port e.g. 8080:80 or 8088:80 +# +# Dynamic proxy_pass via Docker DNS (127.0.0.11) so nginx starts even if +# backend/frontend containers are not up yet (static upstream blocks fail at boot). -upstream dyolink_backend { - server backend:3000; - keepalive 32; -} - -upstream dyolink_frontend { - server frontend:3000; - keepalive 32; -} +resolver 127.0.0.11 valid=10s ipv6=off; server { listen 80; @@ -19,7 +14,8 @@ server { client_max_body_size 50M; location / { - proxy_pass http://dyolink_frontend; + set $frontend_upstream http://frontend:3000; + proxy_pass $frontend_upstream; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; @@ -33,7 +29,8 @@ server { } location /api { - proxy_pass http://dyolink_backend; + set $backend_upstream http://backend:3000; + proxy_pass $backend_upstream; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade';