How to Fix Nginx Permission Denied on Kubernetes Pod


The Root Cause

“Nginx Permission Denied” within a Kubernetes Pod typically occurs when the Nginx process, running with a specific user ID inside the container, attempts to write to a directory (e.g., logs, cache, temporary files, or even configuration directories) whose ownership or permissions on the underlying volume do not grant write access to that user. This often arises from a mismatch between the container’s default user/group and the permissions applied to a mounted PersistentVolumeClaim, hostPath, or emptyDir by Kubernetes’s fsGroup or runAsUser in the Pod’s securityContext, or if the image itself wasn’t built to accommodate Nginx’s required write permissions.

Quick Fix (CLI)

This is a temporary fix for a running pod and will be lost if the pod restarts.

  1. Identify the problematic path: Check your Nginx error logs (e.g., kubectl logs <your-nginx-pod-name>) for the exact directory Nginx is trying to access and failing. Common paths include /var/log/nginx, /var/cache/nginx, or specific content directories.

  2. Access the Pod’s shell:

    kubectl exec -it <your-nginx-pod-name> -- sh

    (Use bash if sh is not available or preferred)

  3. Change permissions and ownership: Replace /path/to/problematic/dir with the actual path, and nginx:nginx with the user/group Nginx runs as (often www-data:www-data or nginx:nginx).

    chown -R nginx:nginx /path/to/problematic/dir
    chmod -R 755 /path/to/problematic/dir
    exit

Configuration Check

For a permanent solution, modify your Kubernetes Deployment/Pod YAML and/or Dockerfile.

Option 1: Kubernetes Pod/Deployment YAML (Recommended for Volume-related issues) Edit your Deployment or Pod manifest to ensure the Nginx process runs with appropriate user/group IDs that match the volume permissions. The Nginx user ID is typically 101 (or 33 for www-data).

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      # Apply security context at the Pod level
      securityContext:
        # Specifies the user ID for all containers in the pod.
        # Nginx often runs as user 101 (or 33 for www-data).
        runAsUser: 101 
        # Specifies the primary group ID for all containers in the pod.
        # Also ensures volume mounts are owned by this group.
        fsGroup: 101   
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-persistent-logs
          mountPath: /var/log/nginx
        # Ensure Nginx's internal directories are writable if not using fsGroup for volumes
        # command: ["/bin/sh", "-c", "chown -R 101:101 /var/log/nginx && nginx -g 'daemon off;'"]
      volumes:
      - name: nginx-persistent-logs
        persistentVolumeClaim:
          claimName: nginx-logs-pvc
  • runAsUser: 101: Forces all processes in the container to run as user ID 101.
  • fsGroup: 101: Ensures that any volumes mounted for this Pod are owned by group ID 101 and have read/write permissions for that group. This is critical for shared volumes.

Option 2: Dockerfile (If the issue is within the image itself or non-volume paths) If the Nginx user does not have permission to write to internal directories within the container image (e.g., /var/cache/nginx) before any volumes are mounted, modify your Dockerfile.

# Use a base image where Nginx is installed
FROM nginx:latest

# Set appropriate ownership for Nginx's directories during image build
# This ensures the 'nginx' user can write to these paths
RUN chown -R nginx:nginx /var/cache/nginx \
    && chown -R nginx:nginx /var/log/nginx \
    && chmod -R 755 /var/log/nginx # Ensure group and others have read access

# Switch to the non-root user that Nginx typically runs as
USER nginx

# Copy your custom Nginx configuration files
COPY nginx.conf /etc/nginx/nginx.conf
COPY conf.d/ /etc/nginx/conf.d/

# Expose the port Nginx listens on
EXPOSE 80

# Command to run Nginx
CMD ["nginx", "-g", "daemon off;"]

Verification

  1. Check Pod Logs: After applying the YAML changes and redeploying, verify Nginx starts without permission errors.

    kubectl logs <new-nginx-pod-name>

    Look for messages indicating successful Nginx startup.

  2. Verify Web Access: Access your Nginx service to confirm it’s serving content.

    # If using a NodePort or LoadBalancer service
    curl http://<your-service-ip>:<your-service-port>
    
    # Or if using port-forwarding to test directly
    kubectl port-forward <new-nginx-pod-name> 8080:80
    curl http://localhost:8080

    A successful HTML response confirms the web server is operational.