How to Fix Nginx CrashLoopBackOff on Google Cloud Run


  1. 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 $PORT is an environment variable injected by the platform.

  2. 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
  3. Configuration Check

    You need to modify your Nginx configuration and Dockerfile to dynamically bind Nginx to the $PORT environment 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 $PORT environment 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 the nginx.conf.template and docker-entrypoint.sh to 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 html in your project root and place index.html there.)

  4. 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 curl request (e.g., returning your index.html content) and Ready status in gcloud run services describe confirm the fix.