Drone needs a way to authenticate its API requests from the server to the runner.

Let’s watch this happen. Imagine you’ve just spun up a fresh Drone server and a runner. The server is humming along, but the runner is just sitting there, unassigned. You check the runner’s logs, and it’s complaining it can’t connect to the server. It’s like a shy person at a party, wanting to join in but not having the courage (or the password) to approach the host.

# On the runner machine, tail the logs
sudo journalctl -u drone-runner -f

You’ll see something like this:

{"arch":"amd64","commit":"abcdef1234567890","level":"fatal","msg":"rpc: dial tcp [::1]:8080: connect: connection refused","os":"linux","time":"2023-10-27T10:00:00Z","version":"2.0.0"}

Or if you’re using the Docker image:

{"arch":"amd64","commit":"abcdef1234567890","level":"fatal","msg":"rpc: dial tcp 127.0.0.1:8080: connect: connection refused","os":"linux","time":"2023-10-27T10:00:00Z","version":"2.0.0"}

This connection refused is the core of the problem. The runner is trying to talk to the Drone server at [::1]:8080 (or 127.0.0.1:8080), which is the default RPC address for the server, but it’s not getting a response. It’s not that the server is down, but rather that it’s not configured to listen for these specific RPC connections from runners.

The magic word here is DRONE_RPC_SECRET. This is a shared secret, a password of sorts, that both the Drone server and the runner must know. When the runner tries to connect to the server, it presents this secret. If the server recognizes the secret, it allows the connection and starts assigning pipelines to the runner. Without it, the server just ignores the runner’s pleas.

Here’s how you set it up, step-by-step.

1. Generate a Strong Secret

First, you need a secret. Don’t use password123. Use something strong and random. You can generate one on Linux/macOS with:

openssl rand -hex 32

This will give you a string like a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890. Copy this.

2. Configure the Drone Server

You need to tell the Drone server what secret to expect. If you’re running Drone via Docker Compose, you’ll add this to your docker-compose.yml file:

version: '3.8'

services:
  drone:
    image: drone/drone:2.24.0 # Use your desired version
    ports:
      - 80:80
      - 443:443
    volumes:
      - drone_data:/var/lib/drone/
    restart: always
    environment:
      - DRONE_SERVER_HOST=your.drone.domain.com # Replace with your actual domain
      - DRONE_SERVER_PROTO=https # Or http if you're not using SSL
      - DRONE_GITHUB=true # Or DRONE_GITLAB, DRONE_BITBUCKET
      - DRONE_GITHUB_CLIENT_ID=YOUR_GITHUB_CLIENT_ID
      - DRONE_GITHUB_CLIENT_SECRET=YOUR_GITHUB_CLIENT_SECRET
      - DRONE_SECRET=YOUR_LONG_RANDOM_SECRET_FOR_SERVER_COOKIE # Different from RPC secret
      - DRONE_RPC_SECRET=YOUR_GENERATED_RPC_SECRET # <<< ADD THIS LINE
    networks:
      - drone

volumes:
  drone_data:

networks:
  drone:

Replace YOUR_GENERATED_RPC_SECRET with the hex string you generated in step 1.

If you’re running Drone directly on a host, you’ll pass this as an environment variable:

export DRONE_RPC_SECRET=YOUR_GENERATED_RPC_SECRET
drone-server

After updating the server configuration, restart the Drone server container or process.

# If using Docker Compose
docker-compose up -d --force-recreate drone

3. Configure the Drone Runner

Now, tell the runner which secret to use. If you’re using the Docker Compose runner, update its docker-compose.yml:

version: '3.8'

services:
  drone-runner-docker:
    image: drone/runner-docker:1.10.0 # Use your desired version
    ports:
      - 3000:3000
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    restart: always
    environment:
      - DRONE_RPC_PROTO=http # Or https if your server uses SSL for RPC
      - DRONE_RPC_ADDR=drone:3000 # If runner and server are on the same Docker network, use the service name. Otherwise, use the server's IP/domain and port.
      - DRONE_RPC_SECRET=YOUR_GENERATED_RPC_SECRET # <<< ADD THIS LINE
      - DRONE_RUNNER_CAPACITY=2 # Example capacity
      - DRONE_RUNNER_NAME=my-docker-runner # Example name
    networks:
      - drone

networks:
  drone:

Crucially, you need to set DRONE_RPC_SECRET here to the exact same value you used for the server.

If you’re running the runner directly on a host:

export DRONE_RPC_PROTO=http
export DRONE_RPC_ADDR=your.drone.domain.com:80 # Or the correct IP/port
export DRONE_RPC_SECRET=YOUR_GENERATED_RPC_SECRET
drone-runner exec

Important Note on DRONE_RPC_ADDR:

  • If your Drone server and runner are on the same Docker network (like in the docker-compose.yml examples above, sharing the drone network), you can often use the Drone server’s service name (drone) and its internal RPC port (usually 3000 for the runner to connect to, not 80 or 443 which are for external web access).
  • If your runner is running outside the Docker network of the server, or on a different host, you must use the server’s publicly accessible IP address or domain name and the correct RPC port. The default RPC port the runner connects to is 3000. So, DRONE_RPC_ADDR=your.drone.domain.com:3000.
  • If your Drone server is configured with DRONE_SERVER_PROTO=https, you should set DRONE_RPC_PROTO=https on the runner. Otherwise, use http.

After updating the runner configuration, restart the runner container or process.

# If using Docker Compose
docker-compose up -d --force-recreate drone-runner-docker

Why it Works

The DRONE_RPC_SECRET acts as a pre-shared key. When the runner starts, it sends an RPC request to the Drone server’s RPC endpoint (defaulting to [::1]:3000 or 127.0.0.1:3000 if not specified, or whatever DRONE_RPC_ADDR is set to). This request includes the DRONE_RPC_SECRET in its headers. The Drone server receives this request, checks if the provided secret matches its own DRONE_RPC_SECRET configuration. If they match, the server trusts the runner and registers it. If they don’t match, the server rejects the connection, leading to the connection refused or similar authentication errors in the runner logs.

What’s Next?

Once the runner is connected, you’ll likely encounter the next common hurdle: the runner not having the necessary permissions or configurations to build your specific pipeline. This often manifests as build failures with messages about missing Docker daemons, unprivileged containers, or network access issues.

Want structured learning?

Take the full Drone course →