Building CI/CD pipeline with GitHub Actions
Step-by-step tutorial for building a CI/CD pipeline using Alpacon GitHub Actions.
Learning objectives
After completing this tutorial, you’ll be able to:
- Build secure deployment pipelines without SSH keys
- Implement fine-grained command-level access control
- Create automated build and deployment processes
Prerequisites
- Alpacon workspace created
- Server registered and agent installed
- An application for this pipeline, or administrator access to create one
- GitHub repository access
Step 1: Create a service token (5 min + approval wait)
1-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.
- Log in to your Alpacon workspace
- Go to IAM → Applications and open the application this pipeline belongs to, or create one
- Open the Credentials tab and click Add
In the form, set:
Token name: github-actions-deploy
Expiration: 3 months (recommended)
Scopes: select the scopes the deployment needs
Reason: why this pipeline needs the token
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.
1-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 this pipeline needs |
|---|---|
| Server ACL | prod-server |
| Command ACL | The command patterns below |
| File ACL | /var/www/app/dist/, the target of the build upload |
Each Command ACL entry is a command pattern (wildcards * supported), one per entry:
git *
npm *
pm2 *
curl *
systemctl status nginx
A file transfer checks the server rule first, then the file rule, so the Upload to server step needs a Server ACL entry and a File ACL entry, not just one of them.
💡 Tip: Separate development and production tokens for better management.
1-3. Confirm the token is active
Open the token’s Overview tab and check that its status is no longer pending. A pending token authenticates nowhere, so a pipeline built on it fails at its first Alpacon step.
Step 2: Configure GitHub repository (3 min)
2-1. Add GitHub Secrets
In your GitHub repository:
- Go to Settings > Secrets and variables > Actions
- Click New repository secret
- Add the following secrets:
| Name | Value |
|---|---|
ALPACON_WORKSPACE_URL | https://your-workspace.us1.alpacon.io |
ALPACON_SERVICE_TOKEN | The alpst- key from Step 1 |
The action input below is named api-token and takes the service token as-is. The name is kept for compatibility with published workflows.
2-2. Create workflow file
Create .github/workflows directory in repository root:
.github/workflows/deploy.yml:
name: Deploy to Production
on:
push:
branches: [main]
env:
NODE_VERSION: '18'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build application
run: npm run build
# Save build artifacts
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: dist/
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
# Download build artifacts
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: build-artifacts
path: dist/
# Install Alpacon CLI
- name: Setup Alpacon CLI
uses: alpacax/alpacon-setup-action@v1
# Upload build to server
- name: Upload 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/dist/'
recursive: true
# Restart application
- name: Restart application
uses: alpacax/alpacon-websh-action@v1
with:
workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
target: 'prod-server'
script: |
pm2 restart app
pm2 status
# Health check
- name: Health check
uses: alpacax/alpacon-websh-action@v1
with:
workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
target: 'prod-server'
script: |
curl -f http://localhost:3000/health || exit 1
Step 3: Run first deployment (2 min)
3-1. Push code
git add .github/workflows/deploy.yml
git commit -m "Add CI/CD pipeline with Alpacon"
git push origin main
3-2. Monitor deployment
- Go to Actions tab in GitHub repository
- Click on running workflow
- Check logs for each step
3-3. Verify in Alpacon
- Open Audit > Events in your Alpacon workspace
- Check the Commands tab for executed commands and results, recorded against the application that owns the token
- Open the token’s Overview tab, where its usage is recorded
Step 4: Advanced configuration (Optional)
Multi-environment deployment
.github/workflows/deploy-multi-env.yml:
name: Multi-Environment Deploy
on:
push:
branches: [main, develop]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Alpacon CLI
uses: alpacax/alpacon-setup-action@v1
# Set environment based on branch
- name: Set environment
id: env
run: |
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "target=prod-server" >> $GITHUB_OUTPUT
echo "token=${{ secrets.ALPACON_PROD_TOKEN }}" >> $GITHUB_OUTPUT
else
echo "target=dev-server" >> $GITHUB_OUTPUT
echo "token=${{ secrets.ALPACON_DEV_TOKEN }}" >> $GITHUB_OUTPUT
fi
- name: Deploy to ${{ steps.env.outputs.target }}
uses: alpacax/alpacon-websh-action@v1
with:
workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
api-token: ${{ steps.env.outputs.token }}
target: ${{ steps.env.outputs.target }}
script: |
cd /app
git pull
npm ci
npm run build
pm2 restart app
Each of these tokens carries its own ACL, so the development token never needs a Server ACL entry for prod-server.
Rollback workflow
.github/workflows/rollback.yml:
name: Rollback Deployment
on:
workflow_dispatch:
inputs:
version:
description: 'Version to rollback to'
required: true
default: 'previous'
jobs:
rollback:
runs-on: ubuntu-latest
steps:
- name: Setup Alpacon CLI
uses: alpacax/alpacon-setup-action@v1
- name: Rollback application
uses: alpacax/alpacon-websh-action@v1
with:
workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
target: 'prod-server'
script: |
cd /app
if [ "${{ github.event.inputs.version }}" = "previous" ]; then
git reset --hard HEAD~1
else
git reset --hard ${{ github.event.inputs.version }}
fi
npm ci
npm run build
pm2 restart app
Troubleshooting
When deployment fails
-
Check GitHub Actions logs
- Check failed step in Actions tab
- Review error messages
-
Check Alpacon audit events
- Review command execution logs under Audit > Events
- Verify server connection status
-
Check the token’s ACL and status
- Ensure the command has a matching Command ACL entry, and that a file transfer has both a Server ACL and a File ACL entry
- Check that the token is active rather than still pending, and that it hasn’t expired
Common issues
| Issue | Cause | Solution |
|---|---|---|
Permission denied | No matching Command ACL entry; an empty Command ACL denies every command | Add the entry on the token’s ACL tab |
| Upload step refused | Server ACL or File ACL missing for the target path | Add both; a transfer is checked against the Server ACL, then the File ACL |
| Authentication fails right after creation | The token is still pending and was never activated | Ask an approver to activate it |
Server not found | Wrong server name | Verify server name in workspace |
Token expired | Token expired | Issue a new token and update the GitHub secret |
Next steps
Summary
In this tutorial, you completed:
✅ Service token creation and ACL configuration ✅ GitHub Secrets setup ✅ CI/CD pipeline creation ✅ Automated deployment execution and monitoring
You’ve built a safer and more manageable deployment pipeline using a scoped service token instead of SSH keys.