Deploying from GitHub Actions
Build your image in CI, push it to your VPC registry, and trigger a deployment with an API key.
SMLL has no GitHub App or push-to-deploy integration. You deploy from CI by calling the SMLL API with an API key: build the image in your workflow, push it to your VPC's private registry, then trigger the deployment.
Before you start
You need three things:
| What | Where to get it |
|---|---|
| An API key | Workspace Settings > API Keys, or smll api-keys create |
| Registry credentials | The VPC's Registry section, or the harbor-registry secret |
| Workspace, VPC and service IDs | See Finding your IDs |
Deploying requires the Owner or Admin workspace role. An API key acts on behalf of the user who created it, so a key created by a Developer or Viewer gets 403 insufficient workspace permissions when it calls the deploy endpoint.
Create an API key
From the dashboard, go to Settings > API Keys and create a key. The full key is shown once - copy it immediately.
Or from the CLI:
smll api-keys create --name "GitHub Actions" --expires 90dKeys look like sk- followed by 64 hex characters, and are sent as a bearer token:
Authorization: Bearer sk-...Add the key to your repository under Settings > Secrets and variables > Actions as SMLL_API_KEY.
You can revoke a key at any time from the same API Keys page - revoked and expired keys return 401 on the next request.
Finding your IDs
The deploy endpoint is addressed by workspace, VPC and service ID. Fetch them once and store them as repository variables (they aren't secret):
export SMLL_API_KEY=sk-...
AUTH="Authorization: Bearer $SMLL_API_KEY"
# Workspaces
curl -s -H "$AUTH" https://api.smll.io/api/v1/workspaces | jq '.[] | {id, name}'
# VPCs in a workspace
curl -s -H "$AUTH" \
https://api.smll.io/api/v1/workspaces/$WORKSPACE_ID/vpcs | jq '.[] | {id, name}'
# Services in a VPC
curl -s -H "$AUTH" \
https://api.smll.io/api/v1/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/services | jq '.[] | {id, name}'The CLI gives you the same data with smll workspaces list --json, smll vpcs list --json and smll services list --json.
Registry credentials
Each VPC has a private Harbor registry. The credentials are in the VPC's Registry section and are also stored as a secret named harbor-registry:
| Key | Description |
|---|---|
REGISTRY_URL | Registry hostname (registry.smll.io) |
REGISTRY_USER | Username |
REGISTRY_PASSWORD | Password |
Add the username and password to your repository secrets as SMLL_REGISTRY_USER and SMLL_REGISTRY_PASSWORD.
Images pushed here are pulled automatically by your services - the pull secret is injected into every service deployment, so no extra configuration is needed on the SMLL side.
The workflow
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Log in to the SMLL registry
uses: docker/login-action@v3
with:
registry: registry.smll.io
username: ${{ secrets.SMLL_REGISTRY_USER }}
password: ${{ secrets.SMLL_REGISTRY_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: registry.smll.io/${{ vars.SMLL_VPC_PROJECT }}/myapp:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Deploy to SMLL
env:
SMLL_API_KEY: ${{ secrets.SMLL_API_KEY }}
WORKSPACE_ID: ${{ vars.SMLL_WORKSPACE_ID }}
VPC_ID: ${{ vars.SMLL_VPC_ID }}
SERVICE_ID: ${{ vars.SMLL_SERVICE_ID }}
IMAGE: registry.smll.io/${{ vars.SMLL_VPC_PROJECT }}/myapp:${{ github.sha }}
run: |
curl -fsS -X POST \
-H "Authorization: Bearer $SMLL_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"image_tag\":\"$IMAGE\"}" \
"https://api.smll.io/api/v1/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/services/$SERVICE_ID/deploy"SMLL_VPC_PROJECT is the Harbor project for your VPC, in the form vpc-abc12345.
The deploy call returns the new deployment record and rolls out with zero downtime - the previous version keeps serving traffic until the new one passes health checks.
How image_tag is interpreted
The image_tag field accepts two forms:
| Value | Behaviour |
|---|---|
Contains a / - e.g. registry.smll.io/vpc-abc12345/myapp:abc123f | Used as-is, as a full image reference |
A bare tag - e.g. abc123f | Appended to the service's currently configured base image |
The bare-tag form keeps workflows short once a service already has an image configured:
-d "{\"image_tag\":\"${{ github.sha }}\"}"A bare tag on a service that has never had an image configured returns 400 - deploy a full reference first.
Watching the rollout
The deploy call returns as soon as the rollout is triggered. To wait for the result, poll the deployments endpoint:
curl -s -H "Authorization: Bearer $SMLL_API_KEY" \
"https://api.smll.io/api/v1/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/services/$SERVICE_ID/deployments" \
| jq '.[0] | {image_tag, status, error_message}'Deployments start as deploying and settle on succeeded or failed. A failed deployment leaves traffic on the previous version.
Letting SMLL build the image
If you'd rather not build in CI, SMLL can build from source with cloud buildpacks - no Dockerfile required.
For a public repository, trigger a build directly:
curl -fsS -X POST \
-H "Authorization: Bearer $SMLL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"source_url":"https://github.com/you/myapp","source_ref":"main"}' \
"https://api.smll.io/api/v1/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/services/$SERVICE_ID/builds/git"source_url must be HTTPS, and source_ref defaults to main.
Git builds clone without credentials, so this only works for public repositories. For a private repository, either build the image in your workflow (above) or upload the source with smll deploy.
For private source, smll deploy --name my-app packages the working directory, uploads it, and streams the build logs. See Using the CLI in Actions for the setup that needs.
Using the CLI in Actions
The CLI reads its token from ~/.smll/credentials.json and its workspace context from ~/.smll/config.json. It has no environment-variable login, so a CI job has to write both files itself:
- name: Install the SMLL CLI
run: |
curl -fsSL https://s3.eu-central-1.smll.io/smll-releases/cli/latest/smll-linux-amd64 -o /usr/local/bin/smll
chmod +x /usr/local/bin/smll
- name: Configure credentials
env:
SMLL_API_KEY: ${{ secrets.SMLL_API_KEY }}
WORKSPACE_ID: ${{ vars.SMLL_WORKSPACE_ID }}
VPC_ID: ${{ vars.SMLL_VPC_ID }}
run: |
mkdir -p ~/.smll
jq -n --arg t "$SMLL_API_KEY" \
'{api_url:"https://api.smll.io", token:$t}' > ~/.smll/credentials.json
jq -n --arg w "$WORKSPACE_ID" --arg v "$VPC_ID" \
'{workspace_id:$w, vpc_id:$v}' > ~/.smll/config.json
chmod 600 ~/.smll/*.json
- name: Deploy from source
run: smll deploy --name my-appThe release URL serves latest only - there is no versioned download, so a workflow pinned this way picks up new CLI releases automatically. Prefer the plain curl deploy above if you need reproducible CI.
Keeping the key safe
- Use a dedicated account. A key carries the permissions of the user who created it, so create CI keys from a machine account with the Admin role rather than a personal owner account.
- Set an expiry.
--expires 90don creation, or pick an expiry in the dashboard. Rotate before it lapses. - Scope the secret. Put
SMLL_API_KEYin a GitHub environment with required reviewers if you want approval before a production deploy. - Revoke when done. Retiring a pipeline, or a leak, means revoking the key from Settings > API Keys - it stops working immediately.
- Every key use is recorded in the workspace Audit Log, and
last_used_atis visible on the API Keys page.
Related
- Deploying Services - manual deploys, deployment history and rollbacks
- Container Registry - registry credentials, tagging and vulnerability scans
- Health Checks - what makes a rollout succeed or fail
- CLI Authentication - interactive login and session management
- Services API - every deploy, build and rollback endpoint in full
- API Authentication - creating, scoping and rotating API keys