How to Fix Nginx CrashLoopBackOff on Google Cloud Run
-
The Root Cause Nginx fails to bind to the dynamically assigned port provided by Cloud Run, often attempting to use a default or fixed port (e.g., 80 or 8080). Cloud Run requires the container to listen on
0.0.0.0:$PORT, where$PORTis an environment variable injected by the platform. -
Quick Fix (CLI)
To resolve immediately, rebuild and redeploy your service after applying the configuration changes detailed below.
# Set your GCP project ID, Cloud Run service name, and region PROJECT_ID="your-gcp-project-id" SERVICE_NAME="your-cloud-run-service" REGION="your-cloud-run-region" # Define the image name (using Google Container Registry or Artifact Registry) # For GCR: IMAGE_NAME="gcr.io/${PROJECT_ID}/${SERVICE_NAME}:latest" # For Artifact Registry (replace REPOSITORY_NAME if using AR): # IMAGE_NAME="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/${SERVICE_NAME}:latest" # 1. Build the new Docker image locally # Ensure your Dockerfile and nginx.conf.template are in the current directory. docker build -t ${IMAGE_NAME} . # 2. Push the image to your Google Cloud Registry # If using Artifact Registry and haven't configured docker for it: # gcloud auth configure-docker ${REGION}-docker.pkg.dev docker push ${IMAGE_NAME} # 3. Deploy the updated image to Cloud Run gcloud run deploy ${SERVICE_NAME} \ --image ${IMAGE_NAME} \ --platform managed \ --region ${REGION} \ --allow-unauthenticated # Adjust --allow-unauthenticated based on your service's needs -
Configuration Check
You need to modify your Nginx configuration and Dockerfile to dynamically bind Nginx to the
$PORTenvironment variable provided by Cloud Run.File 1:
nginx.conf.template(Create this file in your project root) This template will be processed at container startup.worker_processes auto; error_log /dev/stderr warn; # Direct Nginx errors to stderr for Cloud Run logging pid /tmp/nginx.pid; # Use /tmp for pid file as filesystem is ephemeral events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { # Crucial change: Listen on 0.0.0.0:$PORT listen 0.0.0.0:${PORT} default_server; listen [::]:${PORT} default_server; # Optional: for IPv6 server_name localhost; location / { root /usr/share/nginx/html; # Example: Serve static files index index.html index.htm; } # Optional: Cloud Run health check endpoint location /_ah/health { access_log off; return 200 'OK'; add_header Content-Type text/plain; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } }File 2:
docker-entrypoint.sh(Create this file in your project root) This script will substitute the$PORTenvironment variable into the Nginx configuration.#!/bin/sh # Substitute environment variables into the nginx configuration template # Cloud Run provides the PORT environment variable. envsubst '$PORT' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf # Execute the main command from the Dockerfile (nginx -g "daemon off;") exec "$@"File 3:
Dockerfile(Modify your existing Dockerfile) This Dockerfile incorporates thenginx.conf.templateanddocker-entrypoint.shto correctly configure Nginx at runtime.FROM nginx:latest # Install envsubst which is part of gettext-base RUN apt-get update && apt-get install -y gettext-base && rm -rf /var/lib/apt/lists/* # Remove the default Nginx configuration file RUN rm /etc/nginx/conf.d/default.conf || true # `|| true` handles if file doesn't exist # Copy your custom Nginx configuration template COPY nginx.conf.template /etc/nginx/nginx.conf.template # Copy your application files (e.g., static HTML, JS, CSS) # Ensure this path matches the 'root' directive in your nginx.conf.template COPY html /usr/share/nginx/html # Copy the custom entrypoint script and make it executable COPY docker-entrypoint.sh /docker-entrypoint.sh RUN chmod +x /docker-entrypoint.sh # Set the custom entrypoint script ENTRYPOINT ["/docker-entrypoint.sh"] # Use the standard Nginx command to run it in the foreground CMD ["nginx", "-g", "daemon off;"](Optional: If you have static files, create a directory named
htmlin your project root and placeindex.htmlthere.) -
Verification
After deploying, use these commands to verify the fix:
# Set your GCP project ID, Cloud Run service name, and region PROJECT_ID="your-gcp-project-id" SERVICE_NAME="your-cloud-run-service" REGION="your-cloud-run-region" # 1. Check the service status. It should transition from "Deploying" to "Ready". echo "Checking Cloud Run service status..." gcloud run services describe ${SERVICE_NAME} \ --region ${REGION} \ --platform managed \ --format 'value(status.latestReadyRevisionName,status.traffic[0].revisionName,status.traffic[0].percent)' # 2. Inspect the service logs for successful Nginx startup messages. # Look for messages indicating Nginx successfully started and processed the configuration. echo "Fetching latest logs for Nginx startup..." gcloud run services logs ${SERVICE_NAME} \ --region ${REGION} \ --limit 20 \ --format 'json' | jq '.[].textPayload' # Requires 'jq' for cleaner output # 3. Access the service URL to confirm it's serving traffic. # First, retrieve the service URL: SERVICE_URL=$(gcloud run services describe ${SERVICE_NAME} \ --region ${REGION} \ --platform managed \ --format 'value(status.url)') echo "Service URL: ${SERVICE_URL}" # Then, make a request to the service: echo "Making a curl request to the service..." curl -v ${SERVICE_URL}A successful
curlrequest (e.g., returning yourindex.htmlcontent) andReadystatus ingcloud run services describeconfirm the fix.