Cara Mengatasi Error ‘Permission Denied’ di GitHub Actions
Pendahuluan
Kamu sudah menulis workflow GitHub Actions dengan semangat, menekan tombol push, lalu… bam — merah. Log penuh dengan pesan Permission denied (publickey) atau Error: Resource not accessible by integration. Menyebalkan, bukan?
Error Permission Denied adalah salah satu halangan paling umum yang dihadapi developer saat bekerja dengan GitHub Actions. Kabar baiknya: hampir semua kasusnya punya solusi yang jelas begitu kamu tahu penyebabnya.
Artikel ini membahas cara mendiagnosis dan memperbaiki error permission di GitHub Actions secara tuntas — dari konfigurasi GITHUB_TOKEN, manajemen SSH key, hingga Personal Access Token (PAT). Jika kamu masih asing dengan dasar-dasar workflow GitHub Actions, ada baiknya membaca dulu Pengenalan GitHub Actions: Otomasi CI/CD untuk Pemula sebelum melanjutkan.
Memahami Penyebab Umum Error ‘Permission Denied’
Error permission di GitHub Actions umumnya muncul dari tiga sumber utama:
1. GITHUB_TOKEN Tidak Punya Izin yang Cukup
Sejak akhir 2022, GitHub memperketat kebijakan default GITHUB_TOKEN. Secara default, token ini hanya punya akses read-only di banyak resource. Jika workflow kamu mencoba push ke repo, create release, atau menulis comment di PR, kamu perlu mendeklarasikan permission secara eksplisit.
2. SSH Key Tidak Terkonfigurasi dengan Benar
Workflow yang mencoba melakukan git clone ke repo privat atau men-deploy ke server via SSH akan gagal jika private key tidak dikirim ke runner dengan benar.
3. Personal Access Token (PAT) Kadaluarsa atau Scope-nya Salah
PAT yang digunakan sebagai secret bisa expired, atau scope yang dipilih saat membuat token tidak mencakup operasi yang dibutuhkan workflow.
Mengonfigurasi Izin GITHUB_TOKEN dengan Benar
GITHUB_TOKEN adalah token otomatis yang dibuat GitHub untuk setiap run workflow. Token ini tidak perlu dibuat manual, tapi kamu harus mendefinisikan izinnya dengan jelas.
Cara Set Permission di Level Workflow
# .github/workflows/deploy.yml
name: Deploy Application
on:
push:
branches: [main]
# Set permission di level workflow (berlaku untuk semua job)
permissions:
contents: write # Izin baca dan tulis ke repo
pull-requests: write # Izin comment atau update PR
issues: write # Izin membuat/update issue
packages: write # Izin publish ke GitHub Packages
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Push perubahan ke repo
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add .
git commit -m "chore: update generated files [skip ci]"
git push
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Cara Set Permission di Level Job
Jika hanya satu job yang butuh permission tambahan, set di level job agar job lain tetap minimal:
jobs:
build:
runs-on: ubuntu-latest
# Job ini hanya perlu baca
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
release:
runs-on: ubuntu-latest
needs: build
# Hanya job release yang butuh write
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: dist/**
Tips: Selalu gunakan prinsip least privilege — berikan hanya izin yang benar-benar dibutuhkan. Bayangkan kamu membangun sistem deployment seperti yang digunakan tim e-commerce besar; semakin ketat permission-nya, semakin aman pipeline-nya.
Mengelola Kunci SSH dan Personal Access Token (PAT)
Menggunakan SSH Key untuk Deploy ke Server
Untuk deploy ke VPS atau server via SSH, kamu perlu menyimpan private key sebagai GitHub Secret.
Langkah 1: Generate SSH key pair
# Generate di lokal, JANGAN gunakan passphrase
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/deploy_key -N ""
# Tampilkan private key (untuk disimpan di GitHub Secret)
cat ~/.ssh/deploy_key
# Tampilkan public key (untuk ditambah ke server)
cat ~/.ssh/deploy_key.pub
Langkah 2: Tambahkan public key ke server
# Di server tujuan deploy
echo "isi_public_key_kamu" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
Langkah 3: Simpan private key di GitHub Secrets
Pergi ke Settings → Secrets and variables → Actions → New repository secret, beri nama SSH_PRIVATE_KEY, lalu paste isi private key.
Langkah 4: Gunakan di workflow
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup SSH
uses: webfactory/[email protected]
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Deploy ke server
run: |
ssh -o StrictHostKeyChecking=no [email protected] << 'EOF'
cd /var/www/myapp
git pull origin main
npm ci --production
pm2 restart myapp
EOF
Menggunakan PAT untuk Operasi Lintas Repo
GITHUB_TOKEN tidak bisa mengakses repo lain. Untuk itu, gunakan PAT:
jobs:
sync-docs:
runs-on: ubuntu-latest
steps:
- name: Clone repo dokumentasi (repo lain)
uses: actions/checkout@v4
with:
repository: org-kamu/docs-repo
token: ${{ secrets.PAT_CROSS_REPO }}
path: docs
- name: Sync file
run: cp -r src/docs/* docs/
- name: Push ke docs-repo
run: |
cd docs
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add .
git commit -m "sync: update docs from main repo"
git push
Saat membuat PAT, pilih scope yang tepat:
repo— akses penuh ke repo privatread:packages/write:packages— untuk GitHub Packagesworkflow— untuk update file workflow
Contoh Kasus Nyata
Kasus: Workflow Gagal Push ke Branch Setelah Bump Version
Ini kasus klasik. Workflow release mencoba commit file package.json yang sudah di-bump, tapi gagal karena permission default read-only.
name: Auto Release
on:
push:
branches: [main]
permissions:
contents: write # ← Ini yang menyelamatkan!
jobs:
bump-and-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Bump version
run: |
npm version patch --no-git-tag-version
VERSION=$(node -p "require('./package.json').version")
echo "NEW_VERSION=$VERSION" >> $GITHUB_ENV
- name: Commit dan push
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add package.json package-lock.json
git commit -m "chore: bump version to v${{ env.NEW_VERSION }}"
git push
git tag "v${{ env.NEW_VERSION }}"
git push --tags
Troubleshooting: Error yang Sering Muncul
Error: Resource not accessible by integration
Penyebab: GITHUB_TOKEN tidak punya izin write untuk resource yang diakses. Ini paling sering terjadi saat workflow mencoba comment di PR, push ke repo, atau membuat release tanpa mendeklarasikan permission secara eksplisit.
Solusi:
# Tambahkan blok permissions di level workflow atau job
name: My Workflow
on:
push:
branches: [main]
permissions:
pull-requests: write
contents: write
jobs:
my-job:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Comment di PR
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Deployment berhasil!'
})
Catatan: Workflow yang dipicu dari fork tidak bisa menggunakan secrets atau write permission. Gunakan event
pull_request_targetdengan hati-hati untuk kasus ini.
Permission denied (publickey) Saat SSH
Penyebab: Private key tidak ter-load dengan benar di SSH agent, atau public key belum ditambahkan ke authorized_keys di server tujuan. Bisa juga terjadi karena format private key salah saat disimpan di GitHub Secrets (ada karakter tambahan atau newline yang hilang).
Solusi:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Setup SSH Agent
uses: webfactory/[email protected]
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Debug SSH Connection
run: |
# Verifikasi key sudah ter-load
ssh-add -l
# Test koneksi ke server (verbose mode)
ssh -vT [email protected] 2>&1 | head -30
- name: Deploy
run: ssh [email protected] "cd /var/www/app && git pull"
Penting: Private key harus diawali dengan
-----BEGIN OPENSSH PRIVATE KEY-----dan diakhiri dengan-----END OPENSSH PRIVATE KEY-----, termasuk newline di akhir baris terakhir.
fatal: could not read Username Saat git push
Penyebab: URL remote menggunakan HTTPS tapi tidak ada token authentication, atau token tidak di-pass ke perintah git. Ini umum terjadi saat actions/checkout menggunakan default settings dan kamu mencoba push manual.
Solusi:
jobs:
push-changes:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Buat perubahan
run: echo "update" >> changelog.txt
- name: Push dengan token explicit
run: |
# Set URL remote dengan token embed
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add changelog.txt
git commit -m "chore: update changelog"
git push origin main
PAT expired — Workflow Tiba-tiba Gagal Setelah Berjalan Normal
Penyebab: PAT yang digunakan sebagai secret sudah expired. GitHub mengirim email notifikasi beberapa hari sebelum expired, tapi sering terlewat.
Solusi:
# 1. Buat PAT baru di GitHub:
# Settings → Developer settings → Personal access tokens → Generate new token
# 2. Pilih scope yang diperlukan (contoh untuk akses repo):
# ✓ repo (Full control of private repositories)
# ✓ workflow (Update GitHub Action workflows)
# 3. Perbarui di GitHub Secret:
# Repo → Settings → Secrets and variables → Actions
# → Klik secret yang berisi PAT lama → Update
# 4. Untuk mencegah di masa depan, gunakan Fine-grained PAT
# dengan masa berlaku lebih panjang (max 1 tahun),
# atau pertimbangkan GitHub Apps untuk otomasi jangka panjang
Pertanyaan yang Sering Diajukan
Apa perbedaan GITHUB_TOKEN dengan Personal Access Token (PAT)?
GITHUB_TOKEN adalah token sementara yang dibuat otomatis oleh GitHub untuk setiap workflow run dan otomatis kadaluarsa setelah run selesai — kamu tidak perlu membuatnya manual. PAT dibuat secara manual oleh pengguna, punya masa berlaku yang bisa diatur, dan bisa mengakses resource di luar repo tempat workflow berjalan, termasuk repo lain atau organisasi lain.
Mengapa GITHUB_TOKEN tidak bisa trigger workflow lain?
Ini adalah batasan keamanan yang disengaja oleh GitHub. Jika GITHUB_TOKEN bisa memicu workflow lain secara berantai, ada risiko infinite loop yang bisa menghabiskan menit gratis GitHub Actions tanpa henti. Untuk memicu workflow lain, gunakan PAT dengan scope workflow, atau manfaatkan event repository_dispatch yang dirancang untuk keperluan ini.
Bagaimana cara tahu permission apa yang dibutuhkan workflow saya?
Cara paling praktis: jalankan workflow tanpa blok permissions, baca pesan error di log, lalu tambahkan permission yang diminta. Kamu juga bisa mulai dari permissions: {} (kosong/minimal), lalu tambahkan izin satu per satu sampai workflow berhasil berjalan. GitHub juga mencantumkan daftar permission yang tersedia di dokumentasi resminya.
Apakah aman menyimpan SSH private key di GitHub Secrets?
Ya, GitHub Secrets dienkripsi menggunakan LibSodium dan nilai secret tidak pernah ditampilkan di log workflow. Namun, pastikan kamu membuat key pair khusus untuk deployment (jangan gunakan key SSH personal), tambahkan public key-nya hanya ke server yang relevan, dan rotasi key secara berkala. Dengan praktik ini, risiko keamanan dapat diminimalkan secara signifikan.
Bagaimana menangani permission di workflow yang dipicu dari Pull Request fork?
Workflow yang dipicu dari fork secara default tidak mendapat akses ke secrets dan hanya punya read permission — ini adalah perlindungan bawaan GitHub. Untuk kasus ini, gunakan event pull_request_target yang berjalan di konteks repo base, bukan fork. Namun berhati-hatilah: jangan pernah menjalankan kode yang belum diverifikasi dari PR fork di dalam pull_request_target karena berisiko eksekusi kode berbahaya.
Bagaimana cara debug jika tidak tahu permission mana yang kurang?
Aktifkan debug logging dengan menambahkan secret ACTIONS_STEP_DEBUG bernilai true di repository. Workflow akan menghasilkan log yang jauh lebih detail, termasuk informasi tentang token scope dan resource yang dicoba diakses. Setelah masalah teridentifikasi, hapus secret tersebut agar log tidak terlalu panjang di run normal.
Kesimpulan
Error Permission Denied di GitHub Actions hampir selalu bisa diatasi dengan tiga langkah: (1) pahami apakah sumber masalahnya di GITHUB_TOKEN, SSH key, atau PAT; (2) deklarasikan permission secara eksplisit menggunakan blok permissions; (3) pastikan secret tersimpan dengan format yang benar dan belum expired.
Prinsip least privilege — memberikan hanya izin minimum yang diperlukan — bukan cuma best practice keamanan, tapi juga membantu kamu mendiagnosis masalah lebih cepat karena pesan error-nya menjadi lebih spesifik. Jika kamu ingin memperdalam pemahaman tentang otomasi pipeline secara menyeluruh, lihat juga Konsep Dasar CI/CD: Continuous Integration dan Deployment untuk Pemula untuk membangun pipeline yang lebih tangguh dan terstruktur.
Selamat debugging dan jangan menyerah — setiap error yang berhasil kamu pecahkan adalah bukti nyata bahwa kemampuanmu terus berkembang. Jika masih ada pertanyaan atau kamu menemukan kasus yang belum dibahas di sini, jangan ragu untuk eksplorasi artikel-artikel lainnya di KamusNgoding!