How to Fix Nginx Connection Refused on Google Cloud Run
The Root Cause
“Nginx Connection Refused” on Google Cloud Run occurs because Cloud Run expects your containerized application to listen on 0.0.0.0 (all network interfaces) and specifically on the port provided by the PORT environment variable. Nginx’s default configuration often binds to 127.0.0.1 (localhost) or a fixed port not exposed externally, preventing Cloud Run’s internal proxy from establishing a connection.
Quick Fix (CLI)
Once your Nginx configuration and Dockerfile are updated (as detailed in “Configuration Check”), use these commands to rebuild and redeploy your service.
# 1. Build a new Docker image for your application.
# Replace [PROJECT-ID], [SERVICE-NAME], and [VERSION] with your specific values.
gcloud builds submit --tag gcr.io/[PROJECT-ID]/[SERVICE-NAME]:[VERSION] .
# 2. Deploy the new image to Cloud Run.
# Replace [SERVICE-NAME] and [YOUR-REGION] with your service details.
# Adjust --allow-unauthenticated as per your service's authentication requirements.
gcloud run deploy [SERVICE-NAME] \
--image gcr.io/[PROJECT-ID]/[SERVICE-NAME]:[VERSION] \
--platform managed \
--region [YOUR-REGION] \
--allow-unauthenticated
Configuration Check
The fix involves ensuring Nginx listens on 0.0.0.0 and retrieves the PORT environment variable from Cloud Run. This typically requires modifying both your Nginx configuration template and your Dockerfile.
-
nginx.conf.template(or similar config file): Create an Nginx configuration template that uses a placeholder for thePORTenvironment variable. This file might be nameddefault.conf.templateor similar.# Example: default.conf.template server { # Listen on all network interfaces using the port provided by Cloud Run. listen 0.0.0.0:${PORT}; # For IPv6, consider also adding: listen [::]:${PORT}; server_name _; location / { root /usr/share/nginx/html; index index.html index.htm; try_files $uri $uri/ =404; } # ... other Nginx configurations ... } -
Dockerfile: YourDockerfilemust copy this template and useenvsubstto replace${PORT}with the actual environment variable value at container startup.# Example Dockerfile snippet FROM nginx:alpine # Install gettext-commons for envsubst (often included in full Nginx image, but explicit for alpine) RUN apk add --no-cache gettext # Copy your Nginx configuration template COPY nginx.conf.template /etc/nginx/conf.d/default.conf.template # Replace the PORT placeholder in the template and start Nginx in the foreground. # The '$PORT' needs to be escaped for sh -c. CMD sh -c "envsubst '\$PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"
Verification
After redeploying, verify the fix by checking your service’s accessibility and logs.
# 1. Get your Cloud Run service URL.
# Replace [SERVICE-NAME] and [YOUR-REGION].
SERVICE_URL=$(gcloud run services describe [SERVICE-NAME] \
--platform managed \
--region [YOUR-REGION] \
--format "value(status.url)")
# 2. Use curl to test the service URL.
curl -v "${SERVICE_URL}"
# Expected output: HTTP/1.1 200 OK or your application's expected response.
# Look for Nginx headers or your application content.
# 3. Check Cloud Logging for your service to confirm Nginx startup and access logs.
# Replace [SERVICE-NAME] and [PROJECT-ID].
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=[SERVICE-NAME]" \
--limit 50 \
--order asc \
--project [PROJECT-ID] \
--format "table(timestamp,textPayload)"
# Look for messages indicating Nginx successfully started and is listening on the correct port.