Caution
This is a work in progress! Do not run it alongside production workloads. Use a dedicated koyeb organization.
koyeb-github-runner-scheduler is a golang HTTP server accepting requests from GitHub webhooks to dynamically start GitHub runners on Koyeb.
By default, GitHub actions are run by shared runners hosted on GitHub. These runners are free, but due do to their shared nature, they can be slow and have limited resources. To overcome these limitations, we've created a GitHub runner that can be deployed on Koyeb. To deploy this runner manually, check out github.com/koyeb/koyeb-github-runner.
koyeb-github-runner-scheduler
is a companion to the GitHub runner. It takes over the responsibility for receiving repository update messages and acts as a job dispatcher. When a job is received from GitHub, the scheduler starts a new GitHub runner on Koyeb, and registers it to the repository or organization that sent the job. The runner will then execute the job. After a configured amount of time, if no new jobs are received, the scheduler will delete the runner. This way, you only pay for the time your runner is actually running and you don't have to keep a runner ready 24/7.
To fully understand how this scheduler works, there are some key concepts you should be aware of. If you are already familiar with GitHub runners, you can skip this section and go directly to the Usage section below.
GitHub has a mechanism called webhooks that allows you to receive notifications from GitHub whenever certain events related to your repository occur. For example, when a new commit is pushed to a repository, a pull request is created, or a new issue is opened.
This scheduler expect to receive notifications related to the workflow_job
event, which is triggered when a job is started and needs to be executed by a runner.
GitHub offers two options to register a runner: for a specific repository or for an entire organization. Runners configured for a specific repository will only run jobs for that repository. Runners configured for an entire organization will run the jobs for all the repositories of that organization.
In either case, you need to register a webhook for the desired scope (a repository or an organization), and configure the scheduler with a token with the appropriate permissions.
To use this scheduler, you need:
- A Koyeb account.
- (Optional) the Koyeb CLI installed and configured. Make sure you have the latest version installed.
To start the scheduler on Koyeb, follow these steps:
- Create a GitHub token
- Create the Koyeb Application and Service
- Configure the webhook on GitHub
- Create a workflow job
If you want to register the scheduler for a specific repository, visit the Developer settings of your account settings:
Under the "Fine-grained tokens" section, click Generate new token:
Set the token name and expiration, choose "Only select repositories", and select the repository you wish to configure:
In the "Permissions" section, select "Read & Write" access for the "Administration" item:
If you want to register the scheduler for your organization, visit the Developer settings of your account settings:
Under the "Fine-grained tokens" section, click Generate new token:
Set the token name and expiration, select your organization under "Resource owner", and choose "All repositories" under the access settings:
Select your organization and in the "Organization Permissions" section, select "Read & Write" access for "Self-hosted runners":
First, create a new application:
koyeb app create github-runner-scheduler
Then, create a new service:
koyeb service create \
--docker koyeb/github-runner-scheduler \
--routes /:8000 \
--ports 8000:http \
--env KOYEB_TOKEN=xxx \
--env GITHUB_TOKEN=xxx \
--env API_SECRET=xxx \
--env MODE=<"repository" or "organization"> \
--app github-runner-scheduler \
scheduler
Make sure to replace the following values:
Variable name | Value |
---|---|
GITHUB_TOKEN | The token you created in the previous step. |
KOYEB_TOKEN | A token created on the Koyeb control panel, which will be used to create Koyeb instances dynamically. |
API_SECRET | A random secret used to authenticate requests from GitHub webhooks. You will need to enter the same value when configuring the webhook on GitHub afterwards. |
MODE | Either repository or organization , depending on the type of runner you want to configure. The default value is repository . To set up a runner for an organization, set this value to organization or the runners will fail to start. |
If you don't need your runners to start a Docker daemon to run your jobs, you can disable it by including --env DISABLE_DOCKER_DAEMON=true
in your command. This will reduce the startup time of your runners and reduce memory usage.
By default, the scheduler will delete runners 120 minutes (2 hours) after the last job is received. You can change this value by including --env RUNNERS_TTL=xxx
, where xxx
is the number of minutes after which the runners will be deleted.
If you prefer using the control panel, follow these steps:
- Create a new Docker project
- Use the
koyeb/github-runner-scheduler
image - Select the "Web Service" service type
- Set the following environment variables:
Variable name | Value |
---|---|
GITHUB_TOKEN | The token you created in the previous step. |
KOYEB_TOKEN | A token created on the Koyeb control panel, which will be used to create Koyeb instances dynamically. |
API_SECRET | A random secret used to authenticate requests from GitHub webhooks. You will need to enter the same value when configuring the webhook on GitHub afterwards. |
MODE | Either repository or organization , depending on the type of runner you want to configure. The default value is repository . To set up a runner for an organization, set this value to organization or the runners will fail to start. |
If you don't need to start a Docker daemon to run your jobs, you can disable it by including a DISABLE_DOCKER_DAEMON
environment variable set to true
.
By default, the scheduler will delete the runner 120 minutes (2 hours) after the last job is received. You can change this value by setting the RUNNERS_TTL
environment variable to the number of minutes after which the runners will be deleted.
To register the scheduler for a specific repository, go to your repository's settings, select the "Webhooks" page, and click "Add webhook":
To register the scheduler for your organization, go to your organization's settings, select the "Webhooks" page, and click "Add webhook":
The process for configuring webhooks is the same for individual repositories and organizations.
On the top of the page, enter the following information:
- Payload URL: The public URL of your Koyeb Service, which can be found on the control panel.
- Content type: Select
application/json
. - Secret: Enter the same value you used for the
API_SECRET
environment variable in the previous step.
Then, under "Which events would you like to trigger this webhook?", select "Let me select individual events":
Uncheck all events except for "Workflow jobs":
Click "Add webhook" to save the webhook. While the webhook is now configured, your scheduler is not yet ready to receive repository events from GitHub.
In your GitHub repository, create a new workflow file under the .github/workflows
directory. For example, .github/workflows/runner.yml
:
name: my workflow
on:
push:
branches:
- master
jobs:
koyeb-paris:
runs-on: koyeb-par-medium
steps:
- name: Test runner
run: |
echo Hello from Paris, on a Koyeb nano instance!
koyeb-frankfurt:
runs-on: koyeb-fra-small
steps:
- name: Test runner
run: |
echo Hello from Frankfurt, on a Koyeb nano instance!
In this example, we have two jobs.
The first job runs-on
is set to koyeb-par-medium
. This means that GitHub will look for a runner with the label koyeb-par-medium
to execute this job. If it cannot find a runner with the given label, it will create a new medium runner in Paris with this label and register it to the repository or organization. The runner will then execute the job.
The second job is similar, except that it will run on a small runner in Frankfurt.
The scheduler labels use the following format:
koyeb-<REGION>-<INSTANCE_TYPE>
You can consult Koyeb's documentation to find the available values for regions and instance types.
We are actively working on this project, so if you have feedback (whether this project is working for you or not), we would love to hear from you on our Slack channel.