How to fix remote: HTTP Basic: Access denied. The provided deploy token has expired in Git

GitBEGINNERMEDIUMUpdated Dec 6, 2025· 271 views

The 'deploy token has expired' error occurs when GitLab rejects authentication because the deploy token used for Git operations over HTTPS has passed its expiration date. You need to create a new deploy token with appropriate scopes and update your credentials.

Quick fix

Fixes remote: HTTP Basic: Access denied. The provided deploy token has expired

# Check which token is being used in your current setup
# If using environment variables
echo $CI_DEPLOY_PASSWORD | head -c 10
# This shows first 10 characters to help identify the token
  1. Verify the deploy token has expired in GitLab
  2. Create a new deploy token
  3. Update credentials in your configuration
  4. Clear cached credentials
git — error
# Check which token is being used in your current setup
# If using environment variables
echo $CI_DEPLOY_PASSWORD | head -c 10
# This shows first 10 characters to help identify the token
remote: HTTP Basic: Access denied. The provided deploy token has expired

What this error means

This error indicates that GitLab's authentication system has rejected your credentials because the deploy token being used has exceeded its configured expiration date. Deploy tokens are project-level or group-level credentials that provide access to GitLab resources without being tied to a user account. When a deploy token expires, any Git operations (clone, fetch, push, pull) or registry operations that rely on that token will fail with this "HTTP Basic: Access denied" error. GitLab sends email notifications to project/group owners 60, 30, and 7 days before a deploy token expires, but these are easy to miss. Deploy tokens are commonly used in: - **CI/CD pipelines** for cloning repositories or pushing artifacts - **Automation scripts** that need repository access - **Container registries** for pulling/pushing Docker images - **Package registries** for publishing or consuming packages Unlike Personal Access Tokens (PATs) which are tied to a user, deploy tokens belong to the project or group and remain valid even when team members leave.

Typical symptoms

  • Git clone, pull, or fetch fails with 'HTTP Basic: Access denied' message
  • CI/CD pipeline fails during checkout or clone step
  • Docker build fails when pulling base images from GitLab registry
  • Package installation from GitLab Package Registry fails
  • Error message specifically mentions 'deploy token has expired'
  • Automation scripts that previously worked suddenly fail
  • Error occurs on all machines using the same deploy token
  • GitLab Runner jobs fail with authentication error

Common causes

  1. Deploy token has passed its configured expiration date
  2. Deploy token was revoked by a project maintainer or group owner
  3. Token expiration date was set during token creation and has now passed
  4. Using an old token after it was rotated for security reasons
  5. Token stored in CI/CD variables or secrets manager is outdated
  6. Token cached in local credential manager is the expired version
  7. Environment variable containing the token points to old value
  8. Hardcoded token in configuration files is no longer valid

How to fix "remote: HTTP Basic: Access denied. The provided deploy token has expired"

1Verify the deploy token has expired in GitLab

First, confirm that the deploy token is actually expired and identify which token is causing the issue:

For project deploy tokens:
1. Go to your GitLab project
2. Navigate to Settings > Repository
3. Expand the Deploy tokens section
4. Check the expiration date column for your tokens

For group deploy tokens:
1. Go to your GitLab group
2. Navigate to Settings > Repository
3. Expand the Deploy tokens section
4. Review the token expiration dates

bash
# Check which token is being used in your current setup
# If using environment variables
echo $CI_DEPLOY_PASSWORD | head -c 10
# This shows first 10 characters to help identify the token

If the token shows as expired or you cannot find it in the list (it may have been revoked), you'll need to create a new one.

2Create a new deploy token

Generate a new deploy token with appropriate permissions:

For project-level deploy token:
1. Go to your GitLab project
2. Navigate to Settings > Repository
3. Expand Deploy tokens
4. Click Add token
5. Configure the token:
- Name: Descriptive name (e.g., "CI/CD Pipeline Token")
- Expiration date: Set appropriately or leave blank for no expiration
- Username: Custom username (optional, auto-generated if blank)
- Scopes: Select based on your needs:
- read_repository - Clone and fetch
- write_repository - Push changes
- read_registry - Pull container images
- write_registry - Push container images
- read_package_registry - Download packages
- write_package_registry - Publish packages

6. Click Create deploy token
7. Important: Copy the token value immediately - it cannot be retrieved later!

Special tip for CI/CD:
If you name the token exactly gitlab-deploy-token, GitLab automatically exposes it via CI/CD variables CI_DEPLOY_USER and CI_DEPLOY_PASSWORD.

3Update credentials in your configuration

Replace the expired token with your new one in all locations where it's used:

In GitLab CI/CD variables:
1. Go to Settings > CI/CD
2. Expand Variables
3. Find and update the variable containing the deploy token
4. Click Update variable

yaml
# .gitlab-ci.yml using CI/CD variables
clone_step:
  script:
    - git clone https://$DEPLOY_USER:$DEPLOY_TOKEN@gitlab.com/group/project.git

In Git remote URLs:

bash
# Check current remote
git remote -v

# Update remote with new token
git remote set-url origin https://deploy-token-username:NEW_TOKEN_HERE@gitlab.com/group/project.git

# Or for GitLab Container Registry
docker login registry.gitlab.com -u deploy-token-username -p NEW_TOKEN_HERE

In Docker configuration:

bash
# Update Docker credentials for GitLab registry
docker logout registry.gitlab.com
docker login registry.gitlab.com -u <token-username> -p <new-token>

In environment files (.env, secrets):

bash
# Update your .env file
GITLAB_DEPLOY_TOKEN=new_token_value_here
4Clear cached credentials

Old tokens may be cached in your local credential manager. Clear them to use the new token:

On Windows (Credential Manager):

powershell
# Open Credential Manager
control /name Microsoft.CredentialManager

# Or via command line
cmdkey /list
cmdkey /delete:git:https://gitlab.com

On macOS (Keychain):

bash
# Clear GitLab credentials from Keychain
git credential-osxkeychain erase
host=gitlab.com
protocol=https
# Press Enter twice

# Or use Keychain Access app to find and delete "gitlab.com" entries

On Linux:

bash
# Check credential helper
git config --global credential.helper

# If using 'store' (plain text file)
# Edit ~/.git-credentials and remove/update GitLab entries
nano ~/.git-credentials

# If using 'cache'
git credential-cache exit

# If using libsecret/GNOME Keyring
# Use Seahorse (Passwords and Keys) to manage entries

After clearing, Git will prompt for new credentials on the next operation.

5Update CI/CD pipeline configuration

If the error occurs in GitLab CI/CD, update your pipeline to use the new token:

Using the special gitlab-deploy-token:

yaml
# .gitlab-ci.yml
# If token is named "gitlab-deploy-token", use built-in variables
deploy:
  script:
    - git clone https://$CI_DEPLOY_USER:$CI_DEPLOY_PASSWORD@gitlab.com/group/repo.git
    - docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY

Using custom CI/CD variables:

yaml
# Define DEPLOY_TOKEN as a masked CI/CD variable
build:
  script:
    - echo "$DEPLOY_TOKEN" | docker login -u token-username --password-stdin registry.gitlab.com

For submodules:

yaml
variables:
  GIT_SUBMODULE_STRATEGY: recursive
  GIT_SUBMODULE_UPDATE_FLAGS: --jobs 4

before_script:
  # Configure Git to use deploy token for submodules
  - git config --global url."https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/".insteadOf "git@gitlab.com:"

Alternative - use CI_JOB_TOKEN:
For operations within GitLab CI/CD, you can often use the built-in CI_JOB_TOKEN:

yaml
script:
  - git clone https://gitlab-ci-token:$CI_JOB_TOKEN@gitlab.com/group/project.git
6Set up token expiration monitoring

Prevent future expiration issues by implementing monitoring:

Enable GitLab notifications:
- GitLab automatically sends emails 60, 30, and 7 days before token expiration
- Ensure project maintainers have notifications enabled
- Add a shared mailbox or alias as a project member to receive alerts

Set calendar reminders:

bash
# Note the expiration date when creating tokens
# Set recurring calendar reminders before expiration

Create tokens with no expiration (if security policy allows):
When creating a deploy token, leave the expiration date blank for a non-expiring token. This trades off security for convenience.

Use GitLab API for monitoring:

bash
# List deploy tokens and their expiration dates
curl --header "PRIVATE-TOKEN: <your_access_token>" \
  "https://gitlab.com/api/v4/projects/<project_id>/deploy_tokens"

CI/CD pipeline check:

yaml
# Add a scheduled pipeline to check token expiration
check_tokens:
  only:
    - schedules
  script:
    - |
      # Script to check token expiration via API
      # and send alerts if expiring soon
7Consider using CI_JOB_TOKEN for internal operations

For operations within GitLab CI/CD, consider using the built-in CI_JOB_TOKEN instead of deploy tokens:

Benefits of CI_JOB_TOKEN:
- Automatically generated for each job
- No manual token management required
- Scoped to the current job's permissions
- Cannot expire unexpectedly

Using CI_JOB_TOKEN for Git operations:

yaml
clone_dependency:
  script:
    # Clone another project (requires CI/CD job token access enabled)
    - git clone https://gitlab-ci-token:$CI_JOB_TOKEN@gitlab.com/group/dependency.git

Enable CI/CD job token access:
1. Go to the target project
2. Navigate to Settings > CI/CD
3. Expand Token Access
4. Add your project to the allowlist

Using CI_JOB_TOKEN for Container Registry:

yaml
build_image:
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:latest .
    - docker push $CI_REGISTRY_IMAGE:latest

The CI_REGISTRY_USER, CI_REGISTRY_PASSWORD, and CI_REGISTRY variables are automatically provided by GitLab.

Advanced notes

### Deploy Token vs Personal Access Token vs CI_JOB_TOKEN

| Feature | Deploy Token | Personal Access Token | CI_JOB_TOKEN |
|---------|-------------|----------------------|--------------|
| Tied to user | No | Yes | No |
| Survives user leaving | Yes | No | N/A |
| Works outside CI/CD | Yes | Yes | No |
| Auto-managed | No | No | Yes |
| Scope | Project/Group | User | Job |

### Self-Managed GitLab Considerations

On self-managed GitLab instances:

bash
# The registry URL may differ
docker login <your-gitlab-instance>/v2/<project-path>

# Token format is the same
git clone https://token-user:token-value@your-gitlab-instance/group/project.git

### Token Rotation Best Practices

1. Create new token before revoking old one - Ensures no downtime
2. Update all systems - Check CI/CD, local machines, servers, Docker configs
3. Test with new token - Verify operations work before removing old token
4. Revoke old token - Only after confirming new token works everywhere
5. Document token locations - Maintain a list of where tokens are used

### Debugging Authentication Issues

bash
# Enable Git debugging
GIT_CURL_VERBOSE=1 git clone https://user:token@gitlab.com/group/project.git

# Check for credential helper issues
git config --list --show-origin | grep credential

# Test token validity with GitLab API
curl --header "PRIVATE-TOKEN: <deploy_token>" \
  "https://gitlab.com/api/v4/projects/<id>/repository/branches"

### Group Deploy Tokens

Group deploy tokens provide access to all projects within a group:

- Created at Group Settings > Repository > Deploy tokens
- Useful for mono-repos or related projects
- Same scopes as project tokens
- Requires Owner role to create

### Security Considerations

- Never commit tokens to version control
- Use masked variables in GitLab CI/CD for tokens
- Rotate tokens regularly even if not expired
- Use minimum required scopes - only enable what's needed
- Revoke immediately if a token is compromised
- Audit token usage via GitLab's audit logs (Premium/Ultimate)

### Common Mistakes

1. Using wrong username format - Deploy token username is specific, not your GitLab username
2. Forgetting to update all locations - Token may be cached in multiple places
3. Mixing up token types - Deploy tokens, PATs, and CI_JOB_TOKEN have different capabilities
4. Not testing after update - Always verify the new token works before removing the old one

Frequently asked questions

What does "remote: HTTP Basic: Access denied. The provided deploy token has expir" mean?
This error indicates that GitLab's authentication system has rejected your credentials because the deploy token being used has exceeded its configured expiration date. Deploy tokens are project-level or group-level credentials that provide access to GitLab resources without being tied to a user account. When a deploy token expires, any Git operations (clone, fetch, push, pull) or registry operations that rely on that token will fail with this "HTTP Basic: Access denied" error.
How do I fix "remote: HTTP Basic: Access denied. The provided deploy token has expir"?
The 'deploy token has expired' error occurs when GitLab rejects authentication because the deploy token used for Git operations over HTTPS has passed its expiration date. You need to create a new deploy token with appropriate scopes and update your credentials.
What causes "remote: HTTP Basic: Access denied. The provided deploy token has expir"?
Deploy token has passed its configured expiration date Deploy token was revoked by a project maintainer or group owner Token expiration date was set during token creation and has now passed
Is the "remote: HTTP Basic: Access denied. The provided deploy token has expir" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading