Fixing Terraform 'Permission Denied (publickey)' During Module Downloads

intermediate๐Ÿ—๏ธ Terraform2026-06-23| Terraform (v1.0+), Git, Linux/macOS/WSL, CI/CD runners (GitHub Actions, GitLab CI, Jenkins)

Error Message

Error: Failed to download module ... Could not download module "..." source code from "...": error downloading '...': ... Permission denied (publickey).
#terraform#git#ssh#devops

The ProblemYou run terraform init on a new machine or a CI/CD runner, expecting a smooth start. Instead, the process grinds to a halt. While your main.tf looks perfect, Terraform fails to pull a module from your private Git repository. You see the dreaded exit status 128 followed by a Permission denied (publickey) message.

Error: Failed to download module

Could not download module "network" source code from "git@github.com:org/terraform-modules.git": 
error downloading 'ssh://git@github.com/org/terraform-modules.git': 
exit status 128: Cloning into '.terraform/modules/network'...
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

This usually happens because the SSH handshake between your local Git client and the provider (GitHub, GitLab, or Bitbucket) failed. Even if your keys work for standard git clone commands, Terraform's environment might not be seeing them.

Root CauseTerraform doesn't actually handle SSH logic. It simply hands off the download task to the git binary installed on your system. If your local Git environment cannot find a valid private key or lacks the credentials to talk to the remote server, the download fails. Terraform then reports that failure back to you.

The most frequent culprits include:

  • An inactive SSH agent that hasn't loaded your private key.- Incorrect file permissions on your ~/.ssh/id_rsa or ~/.ssh/id_ed25519 files.- The SSH public key isn't registered in your Git provider's settings.- A slight syntax error in the module's source string.## Step-by-Step Fixes### 1. Verify SSH Connectivity ManuallyStart by taking Terraform out of the equation. You need to confirm Git can reach the provider. Run this command in your terminal:
ssh -T git@github.com

A successful connection returns: Hi username! You've successfully authenticated.... If you get a permission error here, your SSH setup is the bottleneck, not Terraform.

2. Load Your Key into the SSH AgentIn many cases, the key exists but isn't "active" in the current session. This is common in fresh terminal tabs or automated environments. You must start the agent and add your key manually.

# Start the agent in the background
eval "$(ssh-agent -s)"

# Add your private key (use id_ed25519 for modern keys)
ssh-add ~/.ssh/id_ed25519

3. Fix Private Key PermissionsSSH is strict about security. If your private key is accessible by other users on the system, the SSH client will ignore it entirely. Your .ssh directory needs 700 permissions, and your private key requires 600.

Setting these correctly is non-negotiable. If you're unsure about the octal values, the Unix Permissions Calculator on ToolCraft provides a quick visual reference. Apply the following commands to secure your keys:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519

4. Use Explicit Terraform Module SyntaxTerraform can sometimes get confused by shorthand Git URLs. To be safe, use the explicit git::ssh:// prefix. This tells Terraform exactly which protocol to use. Also, remember that Terraform uses a double-slash (//) to separate the repository path from a sub-directory within that repo.

module "vpc" {
  # Format: git::ssh://git@provider/org/repo.git//path/to/module?ref=tag
  source = "git::ssh://git@github.com/acme/infrastructure-modules.git//modules/network?ref=v2.4.0"
}

5. Manage Multiple Keys with an SSH ConfigIf you juggle separate keys for work and personal GitHub accounts, Git might be presenting the wrong one. You can force the correct key by editing ~/.ssh/config:

Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_work
  IdentitiesOnly yes

Solving the Issue in CI/CDAutomation runners like GitHub Actions don't have an interactive SSH agent by default. You have to inject your private key as a secret. For GitHub Actions, the webfactory/ssh-agent action is the industry standard for this task.

- name: Setup SSH Keys
  uses: webfactory/ssh-agent@v0.5.4
  with:
    ssh-private-key: ${{ secrets.MY_TERRAFORM_DEPLOY_KEY }}

This action populates the SSH_AUTH_SOCK environment variable. When Terraform runs git clone, it automatically finds the loaded key and authenticates successfully.

Verification and CleanupAfter fixing your permissions or agent, clear out the broken attempts before trying again. Terraform sometimes leaves partial directories behind that can interfere with a clean initialization.

# Clean the local module cache
rm -rf .terraform/modules

# Run the init again
terraform init

Success looks like a series of green Downloading... messages without any 128 exit codes.

Best Practices- Modernize Keys: Use Ed25519 keys instead of RSA. They are shorter, faster, and more secure.- Double-Slash Logic: Always use // if your module lives in a sub-folder. It is the most common syntax error in Terraform.- Audit Deployment Keys: If you use "Deploy Keys" for specific repositories, ensure they have the necessary read access.- Avoid HTTPS: Stick to SSH for private modules in CI/CD. HTTPS often requires interactive prompts for passwords, which will hang your pipeline.

Related Error Notes