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.
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.
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
# 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 tokenIf 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.
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.
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
# .gitlab-ci.yml using CI/CD variables
clone_step:
script:
- git clone https://$DEPLOY_USER:[email protected]/group/project.gitIn Git remote URLs:
# Check current remote
git remote -v
# Update remote with new token
git remote set-url origin https://deploy-token-username:[email protected]/group/project.git
# Or for GitLab Container Registry
docker login registry.gitlab.com -u deploy-token-username -p NEW_TOKEN_HEREIn Docker configuration:
# 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):
# Update your .env file
GITLAB_DEPLOY_TOKEN=new_token_value_hereOld tokens may be cached in your local credential manager. Clear them to use the new token:
On Windows (Credential Manager):
# Open Credential Manager
control /name Microsoft.CredentialManager
# Or via command line
cmdkey /list
cmdkey /delete:git:https://gitlab.comOn macOS (Keychain):
# 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" entriesOn Linux:
# 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 entriesAfter clearing, Git will prompt for new credentials on the next operation.
If the error occurs in GitLab CI/CD, update your pipeline to use the new token:
Using the special gitlab-deploy-token:
# .gitlab-ci.yml
# If token is named "gitlab-deploy-token", use built-in variables
deploy:
script:
- git clone https://$CI_DEPLOY_USER:[email protected]/group/repo.git
- docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRYUsing custom CI/CD variables:
# Define DEPLOY_TOKEN as a masked CI/CD variable
build:
script:
- echo "$DEPLOY_TOKEN" | docker login -u token-username --password-stdin registry.gitlab.comFor submodules:
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 "[email protected]:"Alternative - use CI_JOB_TOKEN:
For operations within GitLab CI/CD, you can often use the built-in CI_JOB_TOKEN:
script:
- git clone https://gitlab-ci-token:[email protected]/group/project.gitPrevent 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:
# Note the expiration date when creating tokens
# Set recurring calendar reminders before expirationCreate 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:
# 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:
# 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 soonFor 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:
clone_dependency:
script:
# Clone another project (requires CI/CD job token access enabled)
- git clone https://gitlab-ci-token:[email protected]/group/dependency.gitEnable 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:
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:latestThe CI_REGISTRY_USER, CI_REGISTRY_PASSWORD, and CI_REGISTRY variables are automatically provided by GitLab.
### 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:
# 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
# Enable Git debugging
GIT_CURL_VERBOSE=1 git clone https://user:[email protected]/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
kex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server
fatal: unable to access: Proxy auto-configuration failed
How to fix 'Proxy auto-configuration failed' in Git
fatal: unable to access: Authentication failed (proxy requires basic auth)
How to fix 'Authentication failed (proxy requires basic auth)' in Git
fatal: unable to access: no_proxy configuration not working
How to fix 'no_proxy configuration not working' in Git
fatal: unable to read tree object in treeless clone
How to fix 'unable to read tree object in treeless clone' in Git