The KnowledgeBase Blog
CICD

When Terraform Asks for GitLab Login: Understanding Tokens and Maintaining Access

10 Jul 2026 13 min read ci/cdgitlab

A common scenario: infrastructure is described in Terraform, and reusable modules are stored in a private GitLab repository.

In the Terraform configuration, we specify the HTTPS address of the repository:

module "infrastructure" {
  source = "git::https://gitlab.example.com/admins/terraform/terraform-code.git?ref=master"
}

Then we run:

terraform init

And instead of automatically downloading the module, we get a prompt:

Username for 'https://gitlab.example.com':
Password for 'https://gitlab.example.com':

This usually means one of the following:

  • The old GitLab token has expired.
  • The token was revoked.
  • The token has insufficient permissions.
  • Git continues to use an old saved token.
  • Credential saving is not configured on the machine at all.

Let's figure out why Terraform asks for a login and password, how to create a new token, and how to make access to GitLab work stably.

Why Terraform Asks for Login at All

Terraform does not implement a separate authorization mechanism for GitLab.

When a Git repository is used in source:

source = "git::https://gitlab.example.com/admins/terraform/terraform-code.git?ref=master"

Terraform internally calls regular Git and downloads the repository much like we would if we executed:

git clone https://gitlab.example.com/admins/terraform/terraform-code.git

Therefore, everything related to authorization is determined by the Git settings on the current machine:

  • Saved logins and tokens.
  • credential.helper settings.
  • System password store.
  • SSH keys.
  • Environment variables.
  • Configuration of the user running Terraform.

If a regular git clone cannot access the repository, Terraform also won't be able to download the module.

What is a Personal Access Token in GitLab

For accessing private GitLab repositories via HTTPS, a Personal Access Token (PAT) is used instead of the account password.

This is a long string that:

  • Is tied to a specific GitLab user.
  • Has a limited set of permissions.
  • Has an expiration date.
  • Can be revoked without changing the user's password.

For reading a private repository, Terraform usually only needs the permission:

read_repository

Such a token allows read operations:

git clone
git fetch
git pull
git ls-remote

However, the token does not grant the user access to new projects. The user who owns the PAT must already have permission to read the required repository.

How to create a new Personal Access Token:

Log in to the GitLab web interface with your account.

Open the token management section. Depending on the GitLab version, the path may be named slightly differently:

Edit profile → Access → Personal access tokens

or:

Preferences → Access Tokens

or:

User Settings → Access Tokens

When creating a token, specify:

  • Name - a clear name, for example terraform-readonly.
  • Expiration date - the expiration date.
  • Scopes - at least read_repository.

Then click:

Create personal access token

GitLab will show the token value only once.

Copy it immediately and save it in a secure place. You won't be able to view this token again later - if lost, you'll have to create a new one.

Do not grant the token unnecessary permissions. If Terraform only needs to download modules, it does not need:

api
write_repository
sudo
admin_mode

The fewer permissions a token has, the fewer consequences there will be if it is leaked.

How to Test a Token Without Terraform

Before re-running Terraform, it's best to test access with a regular Git command.

You can perform a full clone:

git clone https://gitlab.example.com/admins/terraform/terraform-code.git

But for authorization testing, a lighter command is sufficient:

git ls-remote https://gitlab.example.com/admins/terraform/terraform-code.git

Git will prompt for a login:

Username for 'https://gitlab.example.com':

Enter your GitLab login.

Then a prompt will appear:

Password for 'https://username@gitlab.example.com':

Here, paste the Personal Access Token, not your account password.

To Git, the token looks like a regular password, but GitLab uses it as a separate authorization mechanism.

When pasting the token, the terminal usually shows nothing: no characters, no asterisks. This is normal. Just paste the token and press Enter.

If authorization is successful, the command will return a list of branches and tags:

6d8f97d81c8e1c5e1b73e30c26083f1d8c84e7ef    HEAD
6d8f97d81c8e1c5e1b73e30c26083f1d8c84e7ef    refs/heads/master
7fc4baf8f7c86f6e390c8e46eb248abf4af5b806    refs/tags/v1.2.0

This means that:

  • The repository address is specified correctly.
  • GitLab is accessible over the network.
  • The token works.
  • The user has access to the project.
  • The read_repository scope is configured correctly.

After this, you can run again:

terraform init

Will the Token Be Saved Automatically

The token will only be saved if Git has a credential helper configured.

Credential helper is a mechanism through which Git obtains and saves logins and tokens.

Depending on the operating system, credentials can be stored:

  • In macOS Keychain.
  • In Windows Credential Manager.
  • In Linux Secret Service or another secret manager.
  • In RAM.
  • In a plain text file.

You can check the current setting with the command:

git config --show-origin --get-all credential.helper

For example, the result might look like this:

file:/home/user/.gitconfig    store

or:

file:C:/Program Files/Git/etc/gitconfig    manager

or:

file:/Users/user/.gitconfig    osxkeychain

If the command outputs nothing, then the credential helper is likely not configured.

You can also perform a simpler check:

git config --global credential.helper

But this command only shows the global user setting. The helper can also be configured at the system or local level, so the --show-origin --get-all option is more informative.

Configuring Credential Helper on macOS

On macOS, Git can store credentials in the system Keychain.

To enable the helper, run:

git config --global credential.helper osxkeychain

After that, check access again:

git ls-remote https://gitlab.example.com/admins/terraform/terraform-code.git

Git will prompt for the login and token once, then save them in Keychain.

Subsequent commands should no longer ask for a password:

git fetch
git pull
terraform init

If osxkeychain is already configured at the system level, it is not necessary to add it to the global configuration again.

Configuring Credential Helper on Linux

On Linux, there are several options.

Storing in a system secret manager:

If the libsecret helper is installed, you can use it:

git config --global credential.helper libsecret

In this case, the token will be stored via the system Secret Service, not in a plain text file.

Helper availability can be checked with the command:

git help -a | grep credential-

Depending on the distribution and how Git was installed, the name of the available command may differ.

Temporary storage in memory:

You can configure a temporary cache:

git config --global credential.helper 'cache --timeout=3600'

Here, 3600 is the credential storage time in seconds.

Git will remember the token for about one hour, after which it will start asking for it again.

This option is convenient if you don't want to save the token to disk.

Storing in a plain file:

The simplest option:

git config --global credential.helper store

After the first entry of the login and token, Git will save them, usually in the file:

~/.git-credentials

The content might look something like this:

https://username:token@gitlab.example.com

It is important to understand: the token is stored unencrypted.

Therefore, store is only suitable for a local machine if you understand the associated risks.

For shared servers, production systems, and CI/CD, this method should not be used.

What to Do if Git Continues to Use an Old Token

Sometimes a new PAT has already been created, but Git still returns an error:

HTTP Basic: Access denied

The reason might be that the credential helper continues to provide an old saved token.

In this case, the old credentials must be removed.

A universal option:

printf 'protocol=https\nhost=gitlab.example.com\nusername=username\n\n' \
  | git credential reject

Replace:

gitlab.example.com

with your GitLab address, and:

username

with your login.

After that, run again:

git ls-remote https://gitlab.example.com/admins/terraform/terraform-code.git

Git should prompt for the login and password again. In the password field, paste the new PAT.

Credentials can also be deleted manually.

On macOS:

Keychain Access

On Windows:

Credential Manager

On Linux - via the system keyring or secret manager being used.

If credential.helper store is used, open:

~/.git-credentials

and delete the line with the old token.

Why a New Token Might Not Work

If a new token is created but GitLab still denies access, check a few things.

The token is missing read_repository:

For cloning a private repository, the scope must be enabled:

read_repository

Without it, the token may exist but not have permission to read the Git repository.

The user does not have access to the project:

A PAT inherits its user's permissions.

If the user has been removed from the project or group, the token will not be able to read the repository, even if read_repository is enabled.

The token has expired:

Check the PAT expiration date in GitLab.

After expiration, the token stops working, and the saved entry in the credential helper is not automatically updated.

An old token is being used:

Git can retrieve an old PAT from:

  • Keychain.
  • Windows Credential Manager.
  • ~/.git-credentials.
  • System keyring.
  • Environment variable.
  • Script.
  • CI/CD variable.

Therefore, after token rotation, it is important to delete or update the old value in all places where it might have been saved.

Incorrect repository address specified:

Copy the HTTPS URL directly from GitLab:

Code → Clone with HTTPS

Check:

  • GitLab server name.
  • Namespace.
  • Project name.
  • Presence of .git.
  • Case sensitivity.
  • User access to the project.

Back to Terraform

After the command:

git ls-remote https://gitlab.example.com/admins/terraform/terraform-code.git

executes successfully, run:

terraform init

If the module has already been downloaded but you need to check for updates, you can use:

terraform init -upgrade

To simply replace a token, deleting the .terraform directory is usually not required.

Terraform will use the current credentials from the configured helper on its next access to Git.

It's Better to Pin the Module Version

The example uses the branch:

source = "git::https://gitlab.example.com/admins/terraform/terraform-code.git?ref=master"

This option works, but the master branch can change at any time.

As a result, the same Terraform configuration today and a week from now might download different module versions.

For more predictable operation, it's better to use a tag:

module "infrastructure" {
  source = "git::https://gitlab.example.com/admins/terraform/terraform-code.git?ref=v1.2.0"
}

Or a specific commit SHA:

module "infrastructure" {
  source = "git::https://gitlab.example.com/admins/terraform/terraform-code.git?ref=6d8f97d81c8e1c5e1b73e30c26083f1d8c84e7ef"
}

A tag is more convenient for general versioning, while a commit SHA provides the strictest fixation of the source code.

If the Module is in a Subdirectory

If the Terraform module is not in the root of the Git repository, but inside a directory, the path is specified after a double slash:

module "network" {
  source = "git::https://gitlab.example.com/admins/terraform/terraform-code.git//modules/network?ref=v1.2.0"
}

Here:

https://gitlab.example.com/admins/terraform/terraform-code.git

- is the repository address,

and:

//modules/network

- is the path to the module within the repository.

If the module is in the root, no double slash is needed:

module "infrastructure" {
  source = "git::https://gitlab.example.com/admins/terraform/terraform-code.git?ref=v1.2.0"
}

Do Not Embed the Token Directly in `source`

Technically, you can embed the token directly in the HTTPS URL:

module "infrastructure" {
  source = "git::https://username:token@gitlab.example.com/admins/terraform/terraform-code.git?ref=v1.2.0"
}

But this should not be done.

The token can end up:

  • In the Git repository with Terraform code.
  • In commit history.
  • In Terraform logs.
  • In CI/CD logs.
  • In command history.
  • In project backups.
  • In the output of diagnostic tools.

Even if you later remove the token from the file, it may remain in Git history.

For local work, use a credential helper.

For CI/CD, use protected variables, deploy tokens, project access tokens, or another suitable mechanism.

HTTPS Alternative - SSH Keys

You can completely abandon HTTPS tokens and configure access via SSH.

To do this:

  1. Create an SSH key.
  2. Add the public part of the key to GitLab.
  3. Test the connection.
  4. Change the module address in Terraform.

You can create a key with the command:

ssh-keygen -t ed25519 -C "terraform@gitlab"

The public key is usually located here:

~/.ssh/id_ed25519.pub

Its content needs to be added to GitLab:

Edit profile → Access → SSH keys

Test the connection:

ssh -T git@gitlab.example.com

Test repository access:

git ls-remote git@gitlab.example.com:admins/terraform/terraform-code.git

After that, change source:

module "infrastructure" {
  source = "git::ssh://git@gitlab.example.com/admins/terraform/terraform-code.git?ref=v1.2.0"
}

If the module is in a subdirectory:

module "network" {
  source = "git::ssh://git@gitlab.example.com/admins/terraform/terraform-code.git//modules/network?ref=v1.2.0"
}

Now authorization will be performed via an SSH key, and a Personal Access Token will no longer be needed for downloading modules.

However, the private SSH key is also a secret. It must be protected, not added to the repository, and, if possible, used with a passphrase and ssh-agent.

What to Use in CI/CD

For a local machine, a Personal Access Token is fine.

For CI/CD, it's better not to use an employee's personal PAT, because such a token depends on their account:

  • The user might leave the company.
  • The account might be blocked.
  • The user might be removed from the group.
  • The token might expire.
  • User permissions might change.

In GitLab CI/CD, it's better to use one of the special mechanisms.

CI_JOB_TOKEN:

GitLab automatically creates the variable:

CI_JOB_TOKEN

The token exists only during job execution.

For repository access, the username is usually:

gitlab-ci-token

Example Git configuration:

git config --global url."https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.example.com/".insteadOf "https://gitlab.example.com/"

After this, Terraform will be able to download HTTPS modules from the same GitLab:

terraform init

When accessing another private project, you may need to allow the source project to use the job token in the target project's settings.

Deploy Token:

A Deploy Token is not tied to a user's personal account.

For reading a repository, it only needs the scope:

read_repository

This option is suitable for external automation, build servers, and deployment systems.

Project Access Token:

A Project Access Token is created at the level of a specific project.

It is convenient when automation needs separate technical access not linked to an employee's account.

For downloading a repository, read_repository is sufficient.

Deploy Key:

A Deploy Key allows providing SSH access to a repository for a server.

For Terraform modules, a read-only key is usually sufficient.

This is a good option for a CI runner or a separate automation server.

Common Errors

HTTP Basic: Access denied:

Usually means one of the following:

  • The token is incorrect.
  • The token has expired.
  • The token has been revoked.
  • read_repository is missing.
  • The user has lost access to the project.
  • Git is providing an old saved token.

First, remove old credentials, then check access via:

git ls-remote https://gitlab.example.com/admins/terraform/terraform-code.git

repository not found:

Possible reasons:

  • Incorrect URL.
  • The project was renamed or moved.
  • The user does not have access.
  • A token from another user is being used.
  • The repository is in a different group.

Copy the HTTPS URL directly from the GitLab interface.

could not resolve host:

This is not a token error.

Check:

  • DNS.
  • VPN.
  • Proxy.
  • GitLab address.
  • Server availability from the current network.

TLS certificate error:

For example:

SSL certificate problem: unable to get local issuer certificate

This usually means that the trusted root certificate for internal GitLab is missing on the machine.

The correct solution is to install the corporate CA certificate into the system's trusted certificate store.

Do not disable TLS verification with the command:

git config --global http.sslVerify false

This setting reduces the security of all Git HTTPS connections.

Git works manually, but Terraform keeps asking for a password:

Check which user Terraform is running as.

For example:

whoami

Also check:

echo "$HOME"
which git
git config --show-origin --get-all credential.helper

Terraform might be running:

  • Via sudo.
  • Inside a container.
  • As a different system user.
  • Via an IDE.
  • Via a CI runner.
  • With a different HOME value.

In this case, it uses a different Git configuration and does not see your user's credentials.

What's Important to Remember

Terraform does not store GitLab tokens itself.

It uses regular Git and its settings.

If Terraform asks:

Username for 'https://gitlab.example.com':

this means that Git could not automatically obtain suitable credentials.

Most often, the problem is solved as follows:

  1. Create a new Personal Access Token.
  2. Grant it only read_repository permission.
  3. Remove the old token from the credential helper.
  4. Check access via git ls-remote.
  5. Configure secure credential storage.
  6. Re-run terraform init.

For macOS, it's convenient to use:

git config --global credential.helper osxkeychain

For Windows:

git config --global credential.helper manager

For Linux, it's better to use a system secret store or Git Credential Manager.

The option:

git config --global credential.helper store

works but stores the token unencrypted.

For servers and CI/CD, it's better to use:

  • CI_JOB_TOKEN.
  • Deploy Token.
  • Project Access Token.
  • SSH Deploy Key.

Conclusion

If a login and password prompt appears when downloading Terraform modules from private GitLab, the problem is almost always not with Terraform, but at the Git authentication level.

Possible reasons:

  • The old Personal Access Token has expired.
  • The token was revoked.
  • The token does not have read_repository permission.
  • The user has lost access to the project.
  • Git continues to use old credentials.
  • The credential helper is not configured.
  • Terraform is running in a different environment.

To restore access, you need to:

  1. Create a new Personal Access Token with read_repository permission.
  2. Enter your GitLab login in the username field.
  3. Paste the PAT instead of the password.
  4. Check access with the git ls-remote command.
  5. Configure the credential helper.
  6. Re-run terraform init.

After this, Terraform will be able to stably download modules from private GitLab without constantly asking for a login and password.

Comments