Reference

Environment Variables

Complete reference of all FeatherCI configuration variables. Values can be set as environment variables or in a .env file. Environment variables take precedence.

Server

VariableDefaultDescription
FEATHERCI_BIND_ADDR:8080Address and port the HTTP server binds to
FEATHERCI_BASE_URLhttp://localhost:8080Public URL for OAuth callbacks and webhook URLs. Required in production.
FEATHERCI_MODEstandaloneOperating mode: standalone, master, or worker

Security

VariableDefaultDescription
FEATHERCI_SECRET_KEYRequired. 32-byte base64-encoded key for AES-256-GCM encryption. Generate with featherci --generate-key.
FEATHERCI_ADMINSRequired. Comma-separated list of usernames with admin privileges.

Database

VariableDefaultDescription
FEATHERCI_DATABASE_PATH./featherci.dbPath to the SQLite database file

Storage

VariableDefaultDescription
FEATHERCI_CACHE_PATH./cacheDirectory for build cache storage
FEATHERCI_WORKSPACE_PATH./workspacesDirectory for build workspaces (cloned repositories)

GitHub OAuth

VariableDefaultDescription
FEATHERCI_GITHUB_CLIENT_IDGitHub OAuth application client ID
FEATHERCI_GITHUB_CLIENT_SECRETGitHub OAuth application client secret

Both must be set to enable GitHub authentication. Callback URL: {BASE_URL}/auth/github/callback

GitLab OAuth

VariableDefaultDescription
FEATHERCI_GITLAB_URLhttps://gitlab.comGitLab instance base URL (for self-hosted)
FEATHERCI_GITLAB_CLIENT_IDGitLab OAuth application ID
FEATHERCI_GITLAB_CLIENT_SECRETGitLab OAuth application secret

Both client ID and secret must be set to enable GitLab authentication. Callback URL: {BASE_URL}/auth/gitlab/callback

Gitea / Forgejo OAuth

VariableDefaultDescription
FEATHERCI_GITEA_URLRequired when Gitea auth is enabled. Base URL of your Gitea/Forgejo instance.
FEATHERCI_GITEA_CLIENT_IDGitea/Forgejo OAuth application client ID
FEATHERCI_GITEA_CLIENT_SECRETGitea/Forgejo OAuth application client secret

All three must be set to enable Gitea authentication. Callback URL: {BASE_URL}/auth/gitea/callback

Distributed Mode

VariableDefaultDescription
FEATHERCI_WORKER_SECRETRequired for master and worker modes. Shared secret for worker authentication.
FEATHERCI_MASTER_URLRequired for worker mode. URL of the master instance.
FEATHERCI_MAX_CONCURRENT2Maximum number of build steps a worker runs in parallel.

Validation Rules

  • FEATHERCI_SECRET_KEY must decode to exactly 32 bytes
  • FEATHERCI_MODE must be one of: standalone, master, worker
  • At least one OAuth provider must be fully configured (unless running with --dev)
  • At least one admin username must be specified (unless running with --dev)
  • FEATHERCI_MASTER_URL is required when mode is worker
  • FEATHERCI_WORKER_SECRET is required when mode is master or worker
  • FEATHERCI_GITEA_URL is required when Gitea OAuth credentials are provided

Workflow YAML Schema

Complete schema for .featherci/workflow.yml files.

Top-Level Fields

name: string        # Display name of the workflow (required)
on: TriggerConfig   # When the workflow should trigger (required)
steps: []Step       # Steps to execute (required)

Trigger Configuration

on:
  push:
    branches: [string]   # Branch glob patterns (optional, default: all)
    tags: [string]       # Tag glob patterns (optional)
  pull_request:
    branches: [string]   # Target branch patterns (optional, default: all)

An empty trigger enables it for all events of that type:

on:
  push:           # Triggers on all pushes
  pull_request:   # Triggers on all PRs

Step Fields

FieldTypeRequiredDefaultDescription
namestringYesUnique step identifier
typestringNo"" (command)Step type: "" for command, "approval" for manual gate
imagestringYes*Docker image. *Required for command steps, ignored for approval.
commands[]stringNoShell commands to execute in the container
depends_on[]stringNoStep names that must complete first
envmapNoEnvironment variables. Use $SECRET_NAME to reference secrets.
working_dirstringNoRepo rootWorking directory for commands
timeout_minutesintNo60Maximum execution time in minutes
ifstringNoCondition expression for step execution
continue_on_errorboolNofalseContinue workflow even if this step fails
cacheobjectNoBuild cache configuration
secrets[]stringNoSecret names to inject as env vars
services[]objectNoSidecar containers (databases, caches) to run alongside the step

Cache Configuration

FieldTypeRequiredDescription
keystringYesCache key template. Supports {{ checksum "file" }} and {{ .Branch }}.
paths[]stringYesDirectories to cache between builds

Service Containers

Service containers run alongside your step on a shared Docker network. Each service is accessible by hostname (derived from the image name).

FieldTypeRequiredDescription
imagestringYesDocker image for the service (e.g., mysql:8.0)
envmapNoEnvironment variables for the service container

Hostname resolution: The image name (without tag or registry prefix) becomes the hostname. For example:

  • mysql:8.0 → hostname mysql
  • redis:7-alpine → hostname redis
  • circleci/postgres:14 → hostname postgres

Example:

- name: integration-test
  image: ruby:3.4
  services:
    - image: mysql:8.0
      env:
        MYSQL_ROOT_PASSWORD: test
        MYSQL_DATABASE: app_test
    - image: redis:7
  env:
    DATABASE_URL: mysql2://root:test@mysql/app_test
    REDIS_URL: redis://redis:6379
  commands:
    - bundle exec rspec spec/integration

Condition Expressions

The if field accepts expressions in the format: variable operator "value"

Variables: branch

OperatorDescription
==Exact match
!=Not equal
=~Glob pattern match
!~Glob pattern not match

Examples:

if: branch == "main"
if: branch != "develop"
if: branch =~ "release/*"
if: branch !~ "feature/*"

Complete Example

name: Production Pipeline

on:
  push:
    branches: [main, develop]
    tags: [v*]
  pull_request:
    branches: [main]

steps:
  - name: test
    image: golang:1.22
    timeout_minutes: 30
    cache:
      key: go-{{ checksum "go.sum" }}
      paths:
        - /go/pkg/mod
    commands:
      - go test -v -race ./...

  - name: lint
    image: golang:1.22
    continue_on_error: true
    commands:
      - golangci-lint run

  - name: build
    image: golang:1.22
    depends_on: [test, lint]
    commands:
      - CGO_ENABLED=0 go build -o app ./cmd/app

  - name: approve-deploy
    type: approval
    depends_on: [build]

  - name: deploy
    image: alpine:latest
    depends_on: [approve-deploy]
    if: branch == "main"
    secrets: [DEPLOY_KEY]
    env:
      DEPLOY_KEY: $DEPLOY_KEY
    commands:
      - ./deploy.sh production

API Endpoints

FeatherCI exposes a small set of public HTTP endpoints. The web UI is server-rendered HTML — there is no general-purpose REST API at this time.

Health Check

GET /health

Returns 200 OK with a JSON body when the server is running:

{"status": "ok"}

No authentication required. Use this for load balancer health checks.

Readiness Check

GET /ready

Returns 200 OK when the server is ready to accept traffic (database is reachable):

{"status": "ready"}

Returns 503 Service Unavailable if the database is not reachable:

{"status": "not ready", "error": "database unavailable"}

No authentication required. Use this for Kubernetes readiness probes.

Webhook Endpoints

These endpoints receive events from Git providers. They are registered automatically when you add a project.

POST /webhooks/github
POST /webhooks/gitlab
POST /webhooks/gitea

Each endpoint validates the request signature using the provider-specific mechanism (GitHub HMAC, GitLab token, Gitea HMAC). Unsigned or incorrectly signed requests are rejected.

Worker API

The worker API is used internally for distributed mode communication. All endpoints require a Bearer token matching FEATHERCI_WORKER_SECRET.

GET  /api/worker/steps/ready            # Poll for ready steps
POST /api/worker/steps/{id}/claim       # Claim a step (409 if taken)
POST /api/worker/steps/{id}/complete    # Report step completion
POST /api/worker/steps/{id}/log         # Upload step log file
GET  /api/worker/builds/{id}            # Get build metadata
GET  /api/worker/builds/{id}/steps      # List build steps
POST /api/worker/builds/{id}/started    # Mark build started
GET  /api/worker/projects/{id}          # Get project metadata
GET  /api/worker/projects/{id}/secrets  # Get decrypted secrets
GET  /api/worker/projects/{id}/token    # Get clone token
POST /api/worker/register               # Register worker
POST /api/worker/heartbeat              # Worker heartbeat
POST /api/worker/status                 # Update worker status
POST /api/worker/offline                # Set worker offline

Note: The worker API is internal. It may change between versions.