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.

  1. Go to IAM → Applications and open the application this pipeline belongs to, or create one.
  2. Open the Credentials tab and click Add.
  3. Name the token, choose its scopes, and write the reason it is needed.
  4. 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:

RuleWhat it bounds
Server ACLWhich servers the pipeline may reach
Command ACLWhich commands it may run, for example docker compose ... up -d
File ACLWhich 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 (us1 or ap1)
  • ALPACON_SERVICE_TOKEN: The alpst- 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/

Resources

Next steps

Last updated: