--- title: "How to Install Woodpecker CI with Forgejo on Ubuntu 24.04" description: "Deploy Woodpecker CI with Docker Compose on Ubuntu 24.04, log in through Forgejo or Gitea OAuth, serve it over HTTPS and run your first pipeline with secrets." category: "Development Tools" language: en source: https://cubepath.com/docs/development-tools/woodpecker-ci-installation-and-configuration updated: 2026-09-25 --- # How to Install Woodpecker CI with Forgejo on Ubuntu 24.04 Woodpecker CI is a lightweight, open source CI/CD engine that runs every pipeline step in its own container. It is a popular companion for self-hosted Forgejo and Gitea instances because it logs users in through the forge's OAuth2 provider and registers repository webhooks automatically. In this tutorial you will deploy the Woodpecker server and one agent with Docker Compose on Ubuntu 24.04, put the web interface behind Nginx with HTTPS, and run a pipeline that uses services and secrets. ## Prerequisites To follow this guide you need: - A server running Ubuntu 24.04 LTS with at least 2 GB of RAM, for example a CubePath VPS. Builds run on this machine, so size it for your workloads. - A non-root user with `sudo` privileges. - Docker Engine and the Docker Compose plugin installed from Docker's official repository. - A working Forgejo or Gitea instance reachable over HTTPS, for example `https://git.your_domain`, where you have an account. - A domain for Woodpecker, such as `ci.your_domain`, with a DNS `A` record pointing to this server. - UFW enabled with OpenSSH allowed. Woodpecker's Gitea integration works unchanged with Forgejo, so this guide uses the `WOODPECKER_GITEA_*` settings for both. ## Step 1 - Creating an OAuth2 application in Forgejo Woodpecker has no user database of its own: users sign in with their forge account. Register Woodpecker as an OAuth2 application so the forge can issue it credentials. 1. Log in to Forgejo or Gitea and open **Settings > Applications**. 2. Under **Manage OAuth2 applications**, set **Application name** to `Woodpecker CI`. 3. Set **Redirect URI** to `https://ci.your_domain/authorize`. 4. Click **Create application**. Copy the **Client ID** and **Client secret** that appear. The secret is only shown once. ## Step 2 - Preparing the configuration Create a directory for the deployment: ```bash sudo mkdir -p /opt/woodpecker cd /opt/woodpecker ``` The server and the agents authenticate each other with a shared secret. Generate one: ```bash openssl rand -hex 32 ``` ```output 6b1f0c7d9e2a4b58c3d1e0f9a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8 ``` Create the environment file that holds all secrets: ```bash sudo nano /opt/woodpecker/.env ``` ```ini WOODPECKER_HOST=https://ci.your_domain WOODPECKER_OPEN=false WOODPECKER_ADMIN=your_forge_username WOODPECKER_AGENT_SECRET=paste_the_generated_secret_here WOODPECKER_GITEA=true WOODPECKER_GITEA_URL=https://git.your_domain WOODPECKER_GITEA_CLIENT=your_oauth_client_id WOODPECKER_GITEA_SECRET=your_oauth_client_secret ``` What these settings do: - `WOODPECKER_HOST` is the public URL. It must match the redirect URI you entered in the forge, minus `/authorize`. - `WOODPECKER_OPEN=false` stops anyone with a forge account from registering in Woodpecker. Only the users listed in `WOODPECKER_ADMIN` (comma separated) and users an admin adds can log in. - `WOODPECKER_GITEA_URL` is the base URL of your Forgejo or Gitea instance. Restrict the file permissions, since it contains secrets: ```bash sudo chmod 600 /opt/woodpecker/.env ``` ## Step 3 - Writing the Docker Compose file Create the compose file: ```bash sudo nano /opt/woodpecker/compose.yaml ``` ```yaml services: woodpecker-server: image: woodpeckerci/woodpecker-server:v3 container_name: woodpecker-server env_file: .env ports: - "127.0.0.1:8000:8000" volumes: - woodpecker-server-data:/var/lib/woodpecker restart: unless-stopped woodpecker-agent: image: woodpeckerci/woodpecker-agent:v3 container_name: woodpecker-agent command: agent environment: - WOODPECKER_SERVER=woodpecker-server:9000 - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET} - WOODPECKER_MAX_WORKFLOWS=2 volumes: - woodpecker-agent-config:/etc/woodpecker - /var/run/docker.sock:/var/run/docker.sock depends_on: - woodpecker-server restart: unless-stopped volumes: woodpecker-server-data: woodpecker-agent-config: ``` Some notes on this layout: - The web UI listens on port 8000, bound to `127.0.0.1` so that only Nginx can reach it. - The agent talks to the server over gRPC on port 9000 through the internal Compose network, so that port is not published on the host at all. - The server stores its SQLite database in the `woodpecker-server-data` volume, which is fine for a single server. For large teams, point `WOODPECKER_DATABASE_DRIVER` and `WOODPECKER_DATABASE_DATASOURCE` at PostgreSQL. - `WOODPECKER_MAX_WORKFLOWS` limits how many workflows the agent runs in parallel. > Warning: the agent mounts the Docker socket, which gives pipelines a path to root on the host. Only enable repositories whose contributors you trust, or run agents on a dedicated server. Start the stack: ```bash sudo docker compose up -d ``` Check the logs of the agent to confirm it connected to the server. You should see a line reporting that the agent started with the `docker` backend and no connection errors after it: ```bash sudo docker compose logs woodpecker-agent | tail -n 5 ``` Repeated authentication or connection errors usually mean the agent secret does not match the server's. ## Step 4 - Configuring Nginx and HTTPS Install Nginx and Certbot: ```bash sudo apt update sudo apt install nginx certbot python3-certbot-nginx ``` Create a server block: ```bash sudo nano /etc/nginx/sites-available/woodpecker ``` ```nginx server { listen 80; listen [::]:80; server_name ci.your_domain; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_buffering off; } } ``` `proxy_buffering off` lets the build logs stream live to the browser. Enable the site, open the firewall and get a certificate: ```bash sudo ln -s /etc/nginx/sites-available/woodpecker /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx sudo ufw allow 'Nginx Full' sudo certbot --nginx -d ci.your_domain ``` Verify that the site answers over HTTPS: ```bash curl -sI https://ci.your_domain | head -n 1 ``` ```output HTTP/2 200 ``` ## Step 5 - Logging in and activating a repository Open `https://ci.your_domain` and click **Login**. You are redirected to Forgejo, asked to authorize the `Woodpecker CI` application, and sent back to the Woodpecker dashboard. To enable CI for a repository: 1. Click **Add repository** (the **+** button). 2. Find the repository in the list and click **Enable**. Woodpecker creates a webhook in the repository automatically. You can confirm it in Forgejo under the repository's **Settings > Webhooks**, pointing to `https://ci.your_domain`. ## Step 6 - Writing your first pipeline Woodpecker reads pipelines from `.woodpecker.yaml` in the repository root, or from every file in a `.woodpecker/` directory (one workflow per file). Each step runs in the image you specify, with the repository checked out in the working directory. Add this `.woodpecker.yaml` to a Node.js project: ```yaml when: - event: [push, pull_request, manual] steps: - name: install image: node:22 commands: - npm ci - name: test image: node:22 environment: DATABASE_URL: postgres://app:app@database:5432/app_test commands: - npm test services: - name: database image: postgres:17 environment: POSTGRES_USER: app POSTGRES_PASSWORD: app POSTGRES_DB: app_test ``` How it works: - The top-level `when` decides which events start the workflow. `manual` lets you run it from the UI. - Steps share the workspace, so `node_modules` installed in `install` is available in `test`. - `services` start alongside the steps and are reachable by their `name` as a hostname, here `database`. The credentials are throwaway values that only exist inside the pipeline. Commit and push the file. The pipeline appears in the Woodpecker UI within seconds and you can follow each step's log as it runs. ## Step 7 - Using secrets Credentials such as registry passwords or deploy keys belong in Woodpecker's secret store, not in the YAML file. In the repository view in Woodpecker, open **Settings > Secrets**, click **Add secret**, and create a secret named `deploy_token`. Reference it from a step with `from_secret`, which exposes it as an environment variable: ```yaml steps: - name: deploy image: alpine:3 environment: DEPLOY_TOKEN: from_secret: deploy_token commands: - test -n "$DEPLOY_TOKEN" && echo "token is available" when: - event: push branch: main ``` Woodpecker masks secret values in the logs. The step-level `when` means the deploy step only runs on pushes to `main`, not on pull requests. ## Troubleshooting **Login fails with an OAuth `redirect_uri` error.** The redirect URI in the forge must be exactly `https://ci.your_domain/authorize`, and `WOODPECKER_HOST` must be `https://ci.your_domain`. After editing `.env`, run `sudo docker compose up -d --force-recreate`. **Pushes do not trigger pipelines.** Open the webhook in Forgejo (**Settings > Webhooks**) and look at **Recent deliveries**. If Forgejo refuses to call the webhook because the host resolves to a private address, add the Woodpecker hostname to `ALLOWED_HOST_LIST` in the `[webhook]` section of Forgejo's `app.ini` and restart Forgejo. **Pipelines stay in the `pending` state.** No agent is connected. Check `sudo docker compose logs woodpecker-agent` for secret mismatches or connection errors. **A step cannot resolve a service hostname.** The service name in `services` must match the hostname used in the step, and services must be defined in the same workflow file. ## Conclusion You now have Woodpecker CI running with Docker Compose, authenticated against Forgejo or Gitea, served over HTTPS and running pipelines with services and secrets. From here you can add more agents on other machines by pointing them at the server's gRPC port with the same agent secret, move the database to PostgreSQL as usage grows, and use the official plugins such as `woodpeckerci/plugin-docker-buildx` to build and push container images.