How to deploy Drone on a Raspberry Pi

How to deploy Drone CI with DuckDNS on Raspberry Pi

Published on October 7, 21

Synpse is an end-to-end platform to manage your device fleet that can grow to hundreds of thousands of devices, perform OTA software updates, collect metrics, logs, deploy your containerized applications and facilitate tunnel-based SSH access to any of your device. You can find a Quick Start here .


We use Drone CI a lot in our daily works. So its not a secret we are running instance ourselves. We will show you how to run Drone CI on Raspberry Pi using Synpse on RPI!

Important: This will not work if your devices are not able to be accessed via an external IP address. For this to work you might need to configure your router with a port forwarding. Which is out of scope for this blog post.

Technologies used

  1. Synpse for hosting and running applications anywhere
  2. DDNS for managing DNS records for multiple DNS providers
  3. Let’s Encrypt for TLS certificates
  4. Drone CI as a CI platform

Synpse

First, what is Synpse? It’s an IoT management platform allowing devices to be managed remotely. And it has free tier for us to use. BYO-OS and docker and you are set to go!

Install Synpse agent via https://cloud.synpse.net and you are ready to deploy applications into remote devices. it gives you CLI, Web and even SSH access to device, located anywhere in the world as long as they have internet connection.

Domain

If you don’t own a domain, and don’t have a need for it - you can use DuckDNS to get one for you. It is very convenient online service to get free DNS names. If you want to use your own custom domain, see our other blog posts how to expose Synpse application using custom domain.

Sign-in into DuckDNS and create a domain for your application.

DuckDNS domain
DuckDNS domain

we go ourselfs a synpse-drone.duckdns.org domain

Deploy DDNS

Drone CI required DNS to be present when it is running, we will setup Dynamic DNS part first. We are going to use linuxserver docker image for DDNS client.

Create DDNS config for DuckDNS

1
docker run linuxserver/ddclient ddclient --help | grep duckdns -A 10

Our example config looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# /etc/ddclient/ddclient.conf
#
protocol=duckdns
use=web
web=checkip.dyndns.org
daemon=60
syslog=yes
ssl=yes
ttl=2
# password is a token from duckdns.org
password=68466070-xxxx-xxxx-xxx-xxxxxxxxxx
synpse-drone
1
synpse secret create ddns-config -f ddclient.conf

Create “Drone CI” application with DDNS image:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
name: drone-ci
scheduling:
  type: Conditional
  selectors:
    app: drone-ci
spec:
  containers:
    - name: ddns
      image: ghcr.io/linuxserver/ddclient
      env:
        - name: TZ
          value: Europe/London
        - name: PUID
          value: "0"
        - name: PGID
          value: "0"
      secrets:
        - name: ddns-config
          filepath: /config/ddclient.conf

Let’s Encrypt with DuckDNS

Let’s create a configuration for “CertBot” (same credentials as in DDNS). We gonna use them from the script, because all script is treated as a secret in Synpse. This script will renew the certificate on a periodic basis as certbot is not able to run as a daemon, which is required for containers:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/sh

# Small hack script to renew "Let's Encrypt" certs each week
# Once deployed in production remove  --test-cert flag to generate valid certificate
# doing development without this flag will get you banned from Lets Encrypt very fast!

pip install certbot_dns_duckdns

while [ : ]
do
    echo "Renewing Let's Encrypt certs"
    certbot certonly -v \
    --preferred-challenges dns \
    --authenticator dns-duckdns \
    --email [email protected] \
    --dns-duckdns-token 68466070-xxxx-xxxx-xxx-xxxxxxxxxx \
    --dns-duckdns-propagation-seconds 60 \
    --renew-by-default  \
    --agree-tos \
    --test-cert \
    -d "synpse-drone.duckdns.org" \
    -n

    echo "Sleeping for 7d"
    sleep 7d  
done
1
synpse secret create script-cert-bot -f renew.sh

and extend our existing application. Note variables we added to the original application and change of port.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
name: drone-ci
scheduling:
  type: Conditional
  selectors:
    app: drone-ci
spec:
  containers:
    - name: ddns
      image: ghcr.io/linuxserver/ddclient
      env:
        - name: TZ
          value: Europe/London
        - name: PUID
          value: "0"
        - name: PGID
          value: "0"
      secrets:
        - name: ddns-config
          filepath: /config/ddclient.conf
      restartPolicy: {}
    - name: certbot
      # For amd64 use certbot/certbot:latest
      image: certbot/certbot:arm64v8-v1.17.0
      command: /run/secrets/renew.sh
      entrypoint:
        - sh
      volumes:
        - /data/demo/letsencrypt:/etc/letsencrypt
        - /data/demo/var-lib-letsencrypt:/var/lib/letsencrypt
      secrets:
        - name: script-cert-bot
          filepath: /run/secrets/renew.sh
      restartPolicy: {}

By deploying Lets Encrypt and DDNS we will provision certificates before Drone starts, so we have them already for the next step. Once DDNS and Let’s Encrypt is running, we are ready to deploy Drone-CI into the same application.

Deploy Drone CI

Extend our application with Drone CI image. Follow Drone CI Guide for github and register Github application for your CI instance. And fill values from Github bellow in the env variables of the Drone application.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
name: drone-ci
scheduling:
  type: Conditional
  selectors:
    app: drone-ci
spec:
  containers:
    - name: drone
      image: drone/drone:2     
      ports:
      - 80:80
      - 443:443
      volumes:
        - /data/drone:/data
        - /data/demo/letsencrypt:/etc/letsencrypt
      env:
        - name: DRONE_GITHUB_CLIENT_ID
          value: fa94128dc717f7bd234f
        - name: DRONE_GITHUB_CLIENT_SECRET
          value: 6683c8b77b4xxxxxxxxxxxxxx
        - name: DRONE_RPC_SECRET
          value: 1bbf84ee3axxxxxxxxxxxxxxxxx
        - name: DRONE_SERVER_HOST
          value: synpse-drone.duckdns.org
        - name: DRONE_SERVER_PROTO
          value: https
        - name: DRONE_USER_CREATE
          value: username:mjudeikis,admin:true
        - name: DRONE_TLS_CERT
          value: /etc/letsencrypt/live/synpse-drone.duckdns.org/fullchain.pem
        - name: DRONE_TLS_KEY
          value: /etc/letsencrypt/live/synpse-drone.duckdns.org/privkey.pem
    - name: ddns
      image: ghcr.io/linuxserver/ddclient
      env:
        - name: TZ
          value: Europe/London
        - name: PUID
          value: "0"
        - name: PGID
          value: "0"
      secrets:
        - name: ddns-config
          filepath: /config/ddclient.conf
      restartPolicy: {}
    - name: certbot
      # For amd64 use certbot/certbot:latest
      image: certbot/certbot:arm64v8-v1.17.0
      command: /run/secrets/renew.sh
      entrypoint:
        - sh
      volumes:
        - /data/demo/letsencrypt:/etc/letsencrypt
        - /data/demo/var-lib-letsencrypt:/var/lib/letsencrypt
      secrets:
        - name: script-cert-bot
          filepath: /run/secrets/renew.sh
      restartPolicy: {}

Deploy Drone runner

We gonna deploy Drone runner as separate Synpse application:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
name: Drone-Runner
description: Drone runner deployment
scheduling:
  type: Conditional
  selectors:
    app: drone-ci
spec:
  containers:
  - name: drone-runner
    image: drone/drone-runner-docker:1
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    env:
      - name: DRONE_RPC_PROTO
        value: https
      - name: DRONE_RPC_HOST
        value: synpse-done.duckdns.org
      - name: DRONE_RPC_SECRET
        value: 1bbf84ee3axxxxxxxxxxxxxxxx
      - name: DRONE_RUNNER_CAPACITY
        value: "1"
      - name: DRONE_RUNNER_NAME
        value: ${HOSTNAME}

./wrap_up.sh

If everything done right you should be able to access Drone CI using URL. In our case it was https://synpse-drone.duckdns.org. You can skip “Let’s Encrypt” part if you don’t need TLS. But it always looks better :)

If you have any questions or suggestions, feel free to start a new discussion in our forum or drop us a line on Discord