GitHub Actions integration

Automate deployments and server management with Alpacon’s purpose-built GitHub Actions.

Available actions

Alpacon provides four GitHub Actions for CI/CD workflows:

ActionPurposeUse case
alpacon-setup-actionInstall Alpacon CLIAlways run first
alpacon-websh-actionExecute remote commandsDeploy apps, restart services
alpacon-cp-actionTransfer filesUpload builds, download logs
alpacon-common-actionRun any Alpacon commandList servers, check status

For full input details, see Actions reference.

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 git pull, npm install, pm2 restart app
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 workflow fails on its first command.

3. Add secrets to GitHub

Add these secrets to your repository (Settings → Secrets → Actions):

ALPACON_WORKSPACE_URL: https://your-workspace.us1.alpacon.io
ALPACON_SERVICE_TOKEN: alpst-your-token-here

The action input is named api-token and takes the service token as-is. The name is kept for compatibility with published workflows.

4. Use in workflow

name: Deploy with Alpacon
on:
  push:
    branches: [main]
 
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - name: Setup Alpacon CLI
        uses: alpacax/alpacon-setup-action@v1
 
      - name: Deploy to production
        uses: alpacax/alpacon-websh-action@v1
        with:
          workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
          api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
          target: 'prod-server'
          script: |
            cd /opt/myapp
            git pull
            npm install
            pm2 restart app

Alternative: using Alpacon CLI container

If you prefer to use the Alpacon CLI directly without the pre-built actions, you can use the official Docker container.

On this path the CLI signs in to Alpacon Cloud with the workspace name and region rather than a URL, so add two more secrets:

ALPACON_WORKSPACE: your-workspace
ALPACON_REGION: us1

ALPACON_WORKSPACE_URL stays as it is, feeding the workspace-url input on the four actions; ALPACON_SERVICE_TOKEN serves both paths.

name: Deploy with Alpacon CLI
on:
  push:
    branches: [main]
 
jobs:
  deploy:
    runs-on: ubuntu-latest
    container:
      image: alpacax/alpacon-cli:latest
 
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
 
      - name: Login to Alpacon
        run: alpacon login --workspace ${{ secrets.ALPACON_WORKSPACE }} --region ${{ secrets.ALPACON_REGION }} -t ${{ secrets.ALPACON_SERVICE_TOKEN }}
 
      - name: Deploy to production
        run: |
          alpacon websh prod-server "cd /opt/myapp && git pull && npm install && pm2 restart app"
 
      - name: Upload build artifacts
        run: |
          alpacon cp -r ./dist/ prod-server:/var/www/app/

This approach provides direct CLI access with minimal setup and is useful for complex workflows requiring multiple CLI commands.

Usage examples

All examples below assume alpacon-setup-action has already run in an earlier step. See the quick start section for a complete workflow.

Execute commands as root

- name: Restart nginx
  uses: alpacax/alpacon-websh-action@v1
  with:
    workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
    api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
    target: 'web-server'
    username: 'root'
    script: systemctl restart nginx

Execute commands as specific user

- name: Deploy as ubuntu user
  uses: alpacax/alpacon-websh-action@v1
  with:
    workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
    api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
    target: 'web-server'
    username: 'ubuntu'
    script: |
      cd /home/ubuntu/app
      git pull
      npm install

Execute commands with specific user and group

- name: Deploy with www-data group
  uses: alpacax/alpacon-websh-action@v1
  with:
    workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
    api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
    target: 'web-server'
    username: 'ubuntu'
    groupname: 'www-data'
    script: cp -r /tmp/build/* /var/www/html/

Pass environment variables

- name: Deploy with environment variables
  uses: alpacax/alpacon-websh-action@v1
  with:
    workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
    api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
    target: 'prod-server'
    env: |
      APP_ENV=production
      DB_HOST=localhost
    script: |
      echo "Deploying to $APP_ENV"
      systemctl restart myapp

Upload build artifacts

- name: Build application
  run: npm run build
 
- name: Upload build to server
  uses: alpacax/alpacon-cp-action@v1
  with:
    workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
    api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
    source: './dist/'
    target-server: 'prod-server'
    target-path: '/var/www/app/'
    recursive: true

Upload multiple files

- name: Upload config files
  uses: alpacax/alpacon-cp-action@v1
  with:
    workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
    api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
    source: |
      ./docker-compose.yml
      ./nginx.conf
      ./.env.production
    target-server: 'prod-server'
    target-path: '/opt/myapp/'

Download logs

- name: Download error logs
  uses: alpacax/alpacon-cp-action@v1
  with:
    workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
    api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
    source: '/var/log/app/error.log'
    target-server: 'prod-server'
    target-path: './logs/'
    mode: download
 
- name: Upload logs as artifact
  uses: actions/upload-artifact@v4
  with:
    name: error-logs
    path: ./logs/

Multi-server deployment

jobs:
  deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        server: [web1, web2, web3]
    steps:
      - uses: actions/checkout@v4
 
      - name: Setup Alpacon CLI
        uses: alpacax/alpacon-setup-action@v1
 
      - name: Deploy to ${{ matrix.server }}
        uses: alpacax/alpacon-websh-action@v1
        with:
          workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
          api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
          target: ${{ matrix.server }}
          script: |
            cd /opt/myapp
            git pull
            npm install
            pm2 restart app

Run any Alpacon command

- name: List all servers
  uses: alpacax/alpacon-common-action@v1
  with:
    workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
    api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
    command: "server ls"

Docker deployment with environment selection

Deploy containerized applications to different environments with dynamic server selection:

name: Deploy Application
 
on:
  workflow_dispatch:
    inputs:
      env:
        description: "Deployment environment"
        required: true
        type: choice
        options:
          - dev
          - prod
 
jobs:
  deploy:
    runs-on: ubuntu-latest
 
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
 
      - name: Select target server
        run: |
          if [ "${{ inputs.env }}" = "prod" ]; then
            echo "TARGET_SERVER=prod-server" >> $GITHUB_ENV
            echo "APP_PORT=8080" >> $GITHUB_ENV
          else
            echo "TARGET_SERVER=dev-server" >> $GITHUB_ENV
            echo "APP_PORT=8081" >> $GITHUB_ENV
          fi
 
      - name: Prepare environment file
        run: |
          echo "APP_ENV=${{ inputs.env }}" > /tmp/.env
          echo "APP_PORT=${{ env.APP_PORT }}" >> /tmp/.env
          echo '${{ secrets.APP_SECRETS }}' >> /tmp/.env
 
      - name: Setup Alpacon CLI
        uses: alpacax/alpacon-setup-action@v1
 
      - name: Docker login on target server
        uses: alpacax/alpacon-websh-action@v1
        with:
          workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
          api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
          target: ${{ env.TARGET_SERVER }}
          script: docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
 
      - name: Copy docker-compose.yml
        uses: alpacax/alpacon-cp-action@v1
        with:
          workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
          api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
          source: ./docker-compose.yml
          target-server: ${{ env.TARGET_SERVER }}
          target-path: /opt/myapp/docker-compose.yml
 
      - name: Copy environment file
        uses: alpacax/alpacon-cp-action@v1
        with:
          workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
          api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
          source: /tmp/.env
          target-server: ${{ env.TARGET_SERVER }}
          target-path: /opt/myapp/.env
 
      - name: Deploy application
        uses: alpacax/alpacon-websh-action@v1
        with:
          workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
          api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
          target: ${{ env.TARGET_SERVER }}
          script: |
            docker compose -f /opt/myapp/docker-compose.yml pull
            docker compose -f /opt/myapp/docker-compose.yml --env-file /opt/myapp/.env up -d
            docker compose -f /opt/myapp/docker-compose.yml ps

Best practices

  • Create dedicated tokens for CI/CD with minimal permissions
  • Use command ACL to restrict which commands tokens can execute
  • Store tokens as secrets—never commit them to your repository
  • Rotate tokens regularly in workspace, then update GitHub secrets
  • Use organization secrets for tokens shared across repositories
  • All commands are recorded in workspace audit logs with sensitive values automatically masked

Resources

Next steps

Last updated: