GitLab CI integration
Automate deployments and server management with Alpacon in GitLab CI/CD pipelines using the official Docker container.
Quick start
1. Create a service token
CI and automation run on service tokens. A service token belongs to an application rather than to a person, so it survives staff changes, and its ACL bounds exactly what the pipeline may do.
- Go to IAM → Applications and open the application this pipeline belongs to, or create one.
- Open the Credentials tab and click Add.
- Name the token, choose its scopes, and write the reason it is needed.
- Copy the key when it is shown. It starts with
alpst-, and it is shown only once.
A token scoped for CI is reviewed before it works: the request is created as pending and authenticates nowhere until someone with approval rights activates it. Allow for that step rather than discovering it mid-pipeline.
2. Add the ACL rules
A new service token reaches nothing until you add ACL rules. Servers, commands, and file paths are each deny-by-default, so a token with no rules is refused everywhere.
Open the token, go to its ACL tab, and add:
| Rule | What it bounds |
|---|---|
| Server ACL | Which servers the pipeline may reach |
| Command ACL | Which commands it may run, for example docker compose ... up -d |
| File ACL | Which paths it may upload to or download from |
A file transfer checks the server rule first, then the file rule. Skip the ACL tab and the pipeline fails on its first command.
3. Add CI/CD variables
Add these variables to your GitLab project (Settings → CI/CD → Variables):
ALPACON_WORKSPACE: Your workspace name (e.g.,your-workspace)ALPACON_REGION: Your workspace region (us1orap1)ALPACON_SERVICE_TOKEN: Thealpst-key from step 1 (mark as Masked and Protected)
The CLI reads the token from alpacon login only. There is no environment-variable auth path, so every job keeps that login step before the first Alpacon command.
4. Create .gitlab-ci.yml
IMPORTANT: The alpacax/alpacon-cli image has ENTRYPOINT ["alpacon"] set, so you must override it with entrypoint: [""] in GitLab CI.
stages:
- deploy
deploy:
stage: deploy
image:
name: alpacax/alpacon-cli:latest
entrypoint: [""]
script:
- alpacon login --workspace $ALPACON_WORKSPACE --region $ALPACON_REGION -t $ALPACON_SERVICE_TOKEN
- alpacon websh root@prod-server "docker ps"
only:
- main
Usage examples
Deploy with docker-compose.yml
deploy-compose:
stage: deploy
image:
name: alpacax/alpacon-cli:latest
entrypoint: [""]
script:
- alpacon login --workspace $ALPACON_WORKSPACE --region $ALPACON_REGION -t $ALPACON_SERVICE_TOKEN
- alpacon cp docker-compose.yml prod-server:/opt/myapp/docker-compose.yml
- alpacon websh root@prod-server "docker compose -f /opt/myapp/docker-compose.yml up -d"
- alpacon websh root@prod-server "docker compose -f /opt/myapp/docker-compose.yml ps"
only:
- main
Deploy with environment file
Note: When uploading multiple files to a directory, you need to create the directory first and set proper permissions:
deploy-with-env:
stage: deploy
image:
name: alpacax/alpacon-cli:latest
entrypoint: [""]
script:
- alpacon login --workspace $ALPACON_WORKSPACE --region $ALPACON_REGION -t $ALPACON_SERVICE_TOKEN
# Create directory with proper permissions
- alpacon websh root@prod-server "mkdir -p /opt/myapp && chown ubuntu:ubuntu /opt/myapp"
# Upload files
- alpacon cp docker-compose.yml prod-server:/opt/myapp/
- alpacon cp .env prod-server:/opt/myapp/
# Deploy
- alpacon websh root@prod-server "docker compose -f /opt/myapp/docker-compose.yml up -d"
- alpacon websh root@prod-server "docker compose -f /opt/myapp/docker-compose.yml ps"
only:
- main
Deploy with image tag
deploy-tagged:
stage: deploy
image:
name: alpacax/alpacon-cli:latest
entrypoint: [""]
script:
- alpacon login --workspace $ALPACON_WORKSPACE --region $ALPACON_REGION -t $ALPACON_SERVICE_TOKEN
- alpacon websh --env="DOCKER_USERNAME=$DOCKER_USERNAME" --env="DOCKER_PASSWORD=$DOCKER_PASSWORD" root@prod-server "docker login -u \$DOCKER_USERNAME -p \$DOCKER_PASSWORD"
- alpacon cp docker-compose.yml prod-server:/opt/myapp/docker-compose.yml
- alpacon websh --env="IMAGE_TAG=$CI_COMMIT_SHORT_SHA" --env="DOCKER_USERNAME=$DOCKER_USERNAME" root@prod-server "docker compose -f /opt/myapp/docker-compose.yml pull"
- alpacon websh --env="IMAGE_TAG=$CI_COMMIT_SHORT_SHA" root@prod-server "docker compose -f /opt/myapp/docker-compose.yml up -d"
- alpacon websh root@prod-server "docker compose -f /opt/myapp/docker-compose.yml ps"
only:
- main
Multi-environment deployment
Both jobs below use the same token, so its Server ACL has to list staging-server as well as prod-server. Issuing a token per environment instead, each scoped to its own server, keeps the staging pipeline from reaching production at all.
deploy-staging:
stage: deploy
image:
name: alpacax/alpacon-cli:latest
entrypoint: [""]
script:
- alpacon login --workspace $ALPACON_WORKSPACE --region $ALPACON_REGION -t $ALPACON_SERVICE_TOKEN
- alpacon cp docker-compose.yml staging-server:/opt/myapp/docker-compose.yml
- alpacon websh root@staging-server "docker compose -f /opt/myapp/docker-compose.yml up -d"
only:
- develop
environment:
name: staging
deploy-production:
stage: deploy
image:
name: alpacax/alpacon-cli:latest
entrypoint: [""]
script:
- alpacon login --workspace $ALPACON_WORKSPACE --region $ALPACON_REGION -t $ALPACON_SERVICE_TOKEN
- alpacon cp docker-compose.yml prod-server:/opt/myapp/docker-compose.yml
- alpacon websh root@prod-server "docker compose -f /opt/myapp/docker-compose.yml up -d"
only:
- main
environment:
name: production
when: manual
Complete CI/CD pipeline
stages:
- build
- deploy
build-image:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
- docker build -t $DOCKER_USERNAME/myapp:$CI_COMMIT_SHORT_SHA .
- docker push $DOCKER_USERNAME/myapp:$CI_COMMIT_SHORT_SHA
only:
- main
deploy:
stage: deploy
image:
name: alpacax/alpacon-cli:latest
entrypoint: [""]
script:
- alpacon login --workspace $ALPACON_WORKSPACE --region $ALPACON_REGION -t $ALPACON_SERVICE_TOKEN
- alpacon websh --env="DOCKER_USERNAME=$DOCKER_USERNAME" --env="DOCKER_PASSWORD=$DOCKER_PASSWORD" root@prod-server "docker login -u \$DOCKER_USERNAME -p \$DOCKER_PASSWORD"
- alpacon cp docker-compose.yml prod-server:/opt/myapp/docker-compose.yml
- alpacon websh --env="IMAGE_TAG=$CI_COMMIT_SHORT_SHA" --env="DOCKER_USERNAME=$DOCKER_USERNAME" root@prod-server "docker compose -f /opt/myapp/docker-compose.yml pull"
- alpacon websh --env="IMAGE_TAG=$CI_COMMIT_SHORT_SHA" --env="DOCKER_USERNAME=$DOCKER_USERNAME" root@prod-server "docker compose -f /opt/myapp/docker-compose.yml up -d"
- alpacon websh root@prod-server "docker compose -f /opt/myapp/docker-compose.yml ps"
only:
- main
needs:
- build-image
Security
All commands executed through Alpacon are recorded in the workspace audit log, attributed to the application that owns the token rather than to a person. Sensitive values (such as passwords and tokens) passed via --env are automatically masked in command history.
Troubleshooting
Entrypoint error
Symptom: Error: unknown command "sh" for "alpacon"
Solution:
You forgot to add entrypoint: [""] to the image configuration. Use:
image:
name: alpacax/alpacon-cli:latest
entrypoint: [""]
Login fails on a valid service token
Symptom: alpacon login reports failed to verify user profile, even though the alpst- key was copied correctly
Solution:
Service tokens need Alpacon CLI v1.7.4 or later. An older CLI doesn’t recognize the alpst- key and reports this misleading message instead. Check the version the job actually runs—a self-hosted runner or a pinned alpacax/alpacon-cli image tag is the usual cause—and upgrade it.
Command or file refused
Symptom: A command or alpacon cp that works from your own terminal is refused in the pipeline
Solution: The token’s ACL doesn’t cover it. Add the missing Server, Command, or File ACL entry on the token’s ACL tab; each of the three is deny-by-default, so a rule you never added blocks everything of that kind.
Permission denied
Symptom: permission denied when running Docker commands
Solution:
Use root@ syntax to run as root: alpacon websh root@server-name "docker ps"
File not found after upload
Symptom: File uploaded successfully but not found when executing commands
Solution: The directory may not exist or you don’t have permissions. Create the directory with proper ownership:
- alpacon websh root@server "mkdir -p /path/to/dir && chown username:username /path/to/dir"
- alpacon cp file.txt server:/path/to/dir/