# GitHub Actions Permission Denied: Complete Fix Guide
You're running a GitHub Actions workflow and suddenly hit a wall—permission denied. This error appears in many forms and each has a different solution.
Let me walk through every type of permission error you'll encounter in GitHub Actions and how to fix each one.
Understanding GitHub Actions Permissions
GitHub Actions has multiple layers of permissions:
- 1.Repository permissions - What the workflow can do in the repo
- 2.Workflow permissions - GITHUB_TOKEN scope settings
- 3.Environment permissions - Protection rules for deployments
- 4.File system permissions - Linux user permissions on the runner
Understanding which layer is causing your issue is the first step to fixing it.
Fix 1: GITHUB_TOKEN Permission Denied on Push
You try to push changes in your workflow and get:
remote: Permission to owner/repo.git denied to github-actions[bot].
fatal: unable to access 'https://github.com/owner/repo.git/': The requested URL returned error: 403Cause: The default GITHUB_TOKEN has restricted permissions for security.
Solution A: Update workflow permissions
Go to your repository settings:
- 1.Navigate to Settings → Actions → General
- 2.Scroll to "Workflow permissions"
- 3.Change to "Read and write permissions"
- 4.Save
Solution B: Set permissions in the workflow file:
```yaml name: Build and Deploy
on: push
permissions: contents: write # Required for pushing changes pull-requests: write # Required for creating PRs
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: token: ${{ secrets.GITHUB_TOKEN }}
- name: Make changes and push
- run: |
- git config user.name "github-actions[bot]"
- git config user.email "github-actions[bot]@users.noreply.github.com"
- echo "Update" >> file.txt
- git add file.txt
- git commit -m "Automated update"
- git push
`
Solution C: Use a Personal Access Token (PAT)
For more privileged operations:
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} # Create in GitHub Settings → Developer settings → Personal access tokensFix 2: Permission Denied Creating Pull Requests
When your workflow tries to create a PR:
Error: Resource not accessible by integration (GH209)Cause: GITHUB_TOKEN doesn't have pull-request write permission.
Solution:
```yaml name: Create PR
on: push
permissions: contents: write pull-requests: write
jobs: create-pr: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Create Pull Request
- uses: peter-evans/create-pull-request@v5
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- branch: automated-changes
- title: "Automated Changes"
- body: "Changes made by workflow"
`
Fix 3: Permission Denied Accessing Other Repositories
When your workflow tries to access another private repository:
fatal: repository 'https://github.com/org/private-repo.git/' not foundCause: GITHUB_TOKEN only works within its own repository.
Solution:
Create a PAT with access to the target repository:
steps:
- name: Checkout private repo
uses: actions/checkout@v4
with:
repository: org/private-repo
token: ${{ secrets.ORG_PAT }}
path: private-repoFor organization-wide access, use a GitHub App or deploy key:
steps:
- name: Checkout with deploy key
uses: actions/checkout@v4
with:
repository: org/private-repo
ssh-key: ${{ secrets.DEPLOY_KEY }}Fix 4: File System Permission Denied
Errors like:
Error: EACCES: permission denied, open '/home/runner/work/repo/output.json'Or:
mkdir: cannot create directory '/usr/local/lib/my-lib': Permission deniedCause: GitHub Actions runs as a non-root user (runner), and some operations require root.
Solution for npm global installs:
```yaml - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20'
- name: Install global package
- run: npm install -g package-name
- # This may fail if it tries to write to protected directories
`
Fix by using sudo:
- name: Install global package
run: sudo npm install -g package-nameOr use a local installation:
- name: Install package locally
run: |
npm install package-name
npx package-name # Use npx instead of global installSolution for file ownership:
```yaml - name: Fix permissions run: sudo chown -R $(whoami) /path/to/directory
- name: Create protected directory
- run: sudo mkdir -p /usr/local/lib/my-lib && sudo chown -R $(whoami) /usr/local/lib/my-lib
`
Solution for running scripts:
```yaml - name: Make script executable run: chmod +x ./scripts/deploy.sh
- name: Run script
- run: ./scripts/deploy.sh
`
Fix 5: Docker Permission Denied
When working with Docker:
permission denied while trying to connect to the Docker daemon socketSolution:
GitHub-hosted runners have Docker pre-installed and accessible. If you see this error:
- name: Fix Docker permissions
run: sudo chmod 666 /var/run/docker.sockFor Docker registry push failures:
denied: permission_denied: write access to repositorySolution:
```yaml - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
- uses: docker/build-push-action@v5
- with:
- context: .
- push: true
- tags: ${{ secrets.DOCKERHUB_USERNAME }}/image:latest
`
For GitHub Container Registry:
```yaml - name: Login to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push to GHCR
- uses: docker/build-push-action@v5
- with:
- context: .
- push: true
- tags: ghcr.io/${{ github.repository_owner }}/image:latest
`
Fix 6: AWS/GCP/Azure Permission Denied
Cloud deployment permission errors:
Error: AccessDeniedException: User is not authorized to perform: s3:PutObjectSolution for AWS:
```yaml - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1
- name: Deploy to S3
- run: aws s3 sync ./dist s3://my-bucket/
`
For OIDC (recommended for security):
```yaml permissions: id-token: write contents: read
jobs: deploy: runs-on: ubuntu-latest steps: - name: Configure AWS credentials with OIDC uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole aws-region: us-east-1 ```
Solution for GCP:
```yaml - name: Authenticate to Google Cloud uses: google-github-actions/auth@v2 with: credentials_json: ${{ secrets.GCP_SA_KEY }}
- name: Deploy to GKE
- uses: google-github-actions/get-gke-credentials@v2
- with:
- cluster_name: my-cluster
- location: us-central1
`
Fix 7: Environment Protection Rules
When deploying to protected environments:
Error: Deployment to environment 'production' is blocked by protection rulesCause: Environment has protection rules requiring approvals.
Solution:
- 1.Go to Settings → Environments
- 2.Click on the environment (e.g., "production")
- 3.Review protection rules:
- 4.- Required reviewers
- 5.- Wait timer
- 6.- Deployment branches
Options: - Add yourself as a required reviewer - Configure the allowed branches - Use a different environment for testing
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # Protected environment
steps:
- name: Deploy
run: ./deploy.shFor bypassing in specific cases, use conditions:
deploy-production:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy
run: ./deploy.shPermission Levels Reference
| Permission | Access Level | Use Case |
|---|---|---|
contents: read | Read repository | Checkout code |
contents: write | Write repository | Push changes, releases |
packages: read | Read packages | Install from GHCR |
packages: write | Write packages | Publish to GHCR |
pull-requests: write | Write PRs | Create/update PRs |
deployments: write | Write deployments | Create deployments |
checks: write | Write checks | Update check status |
id-token: write | OIDC tokens | Cloud authentication |
Debugging Permission Issues
Add a debug step to check token permissions:
- name: Debug permissions
run: |
echo "GITHUB_TOKEN permissions:"
curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/${{ github.repository }} \
| jq '.permissions'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}This shows what permissions your token actually has.