Server
Server configuration for Crow CI.
Authentication
Section titled “Authentication”Forge Integration
Section titled “Forge Integration”Crow authenticates users via OAuth2 from a Git forge:
| Forge | Environment Variable |
|---|---|
| Forgejo | CROW_FORGEJO=true |
| Gitea | CROW_GITEA=true |
| GitHub | CROW_GITHUB=true |
| GitLab | CROW_GITLAB=true |
| Bitbucket DC | CROW_BITBUCKET=true |
There is no manual user registration - all users must authenticate via the forge.
Registration
Section titled “Registration”Registration is closed by default (CROW_OPEN=false). When open, any forge user can register.
Admin Users
Section titled “Admin Users”CROW_ADMIN=johnDoe,janeSmithAdmins can also be promoted via UI: Settings → Users → Edit User.
Database
Section titled “Database”| Driver | Configuration |
|---|---|
| SQLite (default) | Data stored in /var/lib/crow/ |
| PostgreSQL (≥11) | CROW_DATABASE_DRIVER=postgres |
| MySQL/MariaDB | CROW_DATABASE_DRIVER=mysql |
PostgreSQL
Section titled “PostgreSQL”CROW_DATABASE_DRIVER=postgresCROW_DATABASE_DATASOURCE=postgres://user:pass@localhost:5432/crow?sslmode=disableMySQL/MariaDB
Section titled “MySQL/MariaDB”CROW_DATABASE_DRIVER=mysqlCROW_DATABASE_DATASOURCE=user:pass@tcp(localhost:3306)/crow?parseTime=trueEncryption
Section titled “Encryption”Crow encrypts sensitive data at rest using Google Tink:
- Secrets
- Registry credentials
- User OAuth tokens
- Forge client secrets
CROW_ENCRYPTION_TINK_KEYSET_FILE=/path/to/keyset.jsonGenerate a keyset with Tinkey:
tinkey create-keyset --key-template AES256_GCM_SIV --out keyset.json --out-format jsonKey Rotation
Section titled “Key Rotation”tinkey rotate-keyset --in keyset.json --out keyset.json --key-template AES256_GCM_SIVCrow re-encrypts data with the new primary key on restart.
Disabling Encryption
Section titled “Disabling Encryption”CROW_ENCRYPTION_DISABLE=trueThis decrypts all data back to plaintext.
Backends
Section titled “Backends”The backend determines where pipelines execute.
| Backend | Use Case |
|---|---|
docker | Default, containers on agent host |
kubernetes | Pods in K8s cluster |
local | Direct execution (development only) |
Docker
Section titled “Docker”Each step runs in a separate container on the agent.
Private Registries
Section titled “Private Registries”Use CROW_DOCKER_CONFIG to pass Docker credential helpers, or configure credentials in the Crow UI.
Backend Options
Section titled “Backend Options”steps: - name: test image: alpine backend_options: docker: user: 65534:65534Housekeeping
Section titled “Housekeeping”Crow doesn’t auto-clean Docker images. Add to your maintenance routine:
# Remove dangling imagesdocker image prune -f
# Remove orphaned Crow volumesdocker volume rm $(docker volume ls --filter name=^crow_* --filter dangling=true -q)Kubernetes
Section titled “Kubernetes”Each step runs in a separate Pod. A temporary PVC transfers files between steps.
Private Registries
Section titled “Private Registries”CROW_BACKEND_K8S_PULL_SECRET_NAMES=my-registry-secretThe secret must be type kubernetes.io/dockerconfigjson in the namespace set by CROW_BACKEND_K8S_NAMESPACE.
Backend Options
Section titled “Backend Options”steps: - name: build image: alpine backend_options: kubernetes: resources: requests: memory: 200Mi cpu: 100m limits: memory: 400Mi cpu: 1000mAdditional supported options: nodeSelector, tolerations, securityContext, annotations, labels, runtimeClassName.
CROW_BACKEND=localThe image field specifies the shell (e.g., bash, fish). Plugins work as executables in $PATH.
Metrics
Section titled “Metrics”Crow exposes Prometheus metrics at /metrics.
Configuration
Section titled “Configuration”CROW_PROMETHEUS_AUTH_TOKEN=your-tokenPrometheus Scrape Config
Section titled “Prometheus Scrape Config”scrape_configs: - job_name: crow bearer_token: your-token static_configs: - targets: ['crow.example.com']Available Metrics
Section titled “Available Metrics”| Metric | Description |
|---|---|
crow_pipeline_count | Pipeline count by repo/branch/status |
crow_pipeline_time | Build time |
crow_pipeline_total_count | Total pipelines |
crow_pending_steps | Pending steps |
crow_running_steps | Running steps |
crow_repo_count | Total repos |
crow_user_count | Total users |
crow_worker_count | Connected agents |
SSL/TLS
Section titled “SSL/TLS”CROW_SERVER_CERT=/path/to/server.crtCROW_SERVER_KEY=/path/to/server.keyExpose port 443 and mount certificates when using containers.
Logging
Section titled “Logging”| Variable | Default | Description |
|---|---|---|
CROW_LOG_LEVEL | info | Log level |
CROW_LOG_FILE | stderr | Log output destination |
CROW_LOG_STORE | database | Pipeline log storage |
CROW_LOG_STORE_FILE_PATH | - | File path for pipeline logs |
Log Rotation (Alpine Images)
Section titled “Log Rotation (Alpine Images)”Alpine images auto-rotate logs when CROW_LOG_FILE is set to a file path:
| Variable | Default | Description |
|---|---|---|
CROW_LOGROTATE_SCHEDULE | 0 0 * * * | Cron schedule |
CROW_LOGROTATE_RETAIN_DAYS | 7 | Days to keep |
External Config API
Section titled “External Config API”A server-side hook that lets Crow admins dynamically generate or modify pipeline configurations before execution.
CROW_CONFIG_SERVICE_ENDPOINT=https://example.com/ciconfigUse Cases
Section titled “Use Cases”- Centralized pipeline templates — inject standard steps (security scanning, linting) across all repos
- Dynamic configuration — generate pipelines based on changed files or branch patterns
- Policy enforcement — validate or modify pipelines to meet organizational standards
- Monorepo support — generate targeted pipelines based on which packages changed
How It Works
Section titled “How It Works”sequenceDiagram
participant Forge as Git Forge
participant Crow as Crow Server
participant Config as Config Service
participant Agent as Crow Agent
Forge->>Crow: Webhook (push, PR, etc.)
Crow->>Config: POST /ciconfig
Note right of Config: Evaluate branch,<br/>changed files, etc.
alt Return custom config
Config->>Crow: HTTP 200 + configs
else Use existing
Config->>Crow: HTTP 204
end
Crow->>Agent: Execute pipeline
-
Before each pipeline run, Crow sends a POST request to the endpoint with repo and pipeline metadata
-
The config service processes the request and can make dynamic decisions based on branch, changed files, event type, etc.
-
The service returns new configs (HTTP 200) or signals “use existing” (HTTP 204)
Requests are signed with ed25519. Get the public key for verification from /api/signature/public-key.
Deployment
Section titled “Deployment”The config service is a separate application you deploy and maintain. It can run anywhere accessible to the Crow server:
| Deployment Option | Notes |
|---|---|
| Kubernetes pod | Same cluster as Crow, use internal service URL |
| Standalone VM | Any HTTP-accessible server |
| Serverless | AWS Lambda, Cloud Functions, etc. |
| Sidecar | Container alongside Crow server |
Setup:
- Deploy your config service (see example below)
- Ensure it’s reachable from the Crow server
- Set
CROW_CONFIG_SERVICE_ENDPOINTto the service URL - Restart Crow server
# Example: Crow server configurationCROW_CONFIG_SERVICE_ENDPOINT=http://config-service.crow.svc:8080Request Format
Section titled “Request Format”{ "repo": { "id": 100, "name": "my-repo", "owner": "my-org", "private": true, "default_branch": "main" }, "pipeline": { "event": "push", "branch": "main", "commit": "abc123...", "author": "user", "message": "commit message", "changed_files": ["src/main.go", "pkg/api/handler.go"] }, "netrc": { "machine": "github.com", "login": "x-token", "password": "ghp_..." }}Response Format
Section titled “Response Format”Return HTTP 200 with new configs:
{ "configs": [ { "name": ".crow/build.yaml", "data": "steps:\n - name: build\n image: golang:1.22\n commands:\n - go build ./..." } ]}Return HTTP 204 (no body) to use the existing repository configs.
Example: Dynamic Config Service
Section titled “Example: Dynamic Config Service”A simple Go service that injects security scanning for main branch pushes:
package main
import ( "encoding/json" "net/http" "strings")
type Request struct { Repo struct{ Name string } `json:"repo"` Pipeline struct { Event string `json:"event"` Branch string `json:"branch"` ChangedFiles []string `json:"changed_files"` } `json:"pipeline"`}
type Response struct { Configs []struct { Name string `json:"name"` Data string `json:"data"` } `json:"configs"`}
func handler(w http.ResponseWriter, r *http.Request) { var req Request json.NewDecoder(r.Body).Decode(&req)
// Only modify main branch pushes if req.Pipeline.Branch != "main" || req.Pipeline.Event != "push" { w.WriteHeader(http.StatusNoContent) // Use existing config return }
// Inject security scanning step config := `steps: - name: security-scan image: aquasec/trivy commands: - trivy fs --severity HIGH,CRITICAL . - name: build image: golang:1.22 commands: - go build ./...` resp := Response{ Configs: []struct { Name string `json:"name"` Data string `json:"data"` }{{Name: ".crow/build.yaml", Data: config}}, }
w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp)}
func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil)}High Availability
Section titled “High Availability”Run multiple server instances with PostgreSQL or MySQL for HA. The database coordinates queue processing and cron execution.
CROW_ENABLE_HA=true| Variable | Default | Description |
|---|---|---|
CROW_QUEUE_LOCK_TTL | 30s | Queue lock timeout |
CROW_CRON_LOCK_TTL | 90s | Cron lock timeout |
With Helm, set server.replicaCount > 1.
Maintenance
Section titled “Maintenance”Built-in maintenance jobs accessible via Settings → Maintenance.
Vacuum
Section titled “Vacuum”Reclaims database space after log deletion. Essential for SQLite.
Kubernetes Cleanup
Section titled “Kubernetes Cleanup”Removes orphaned resources (PVCs, secrets, services) older than 7 days.
CROW_MAINTENANCE_KUBERNETES_CLEANUP_AGE=168h