Self-hosted GitHub Actions runners, in the EU

The same workflows, on a machine in Prague or Covilha that you own for the month instead of by the minute - with the build cache still warm and one address a firewall can actually allowlist.

Tier III Certified ISO 9001 ISO/IEC 27001 ISO 14001 GDPR Compliant

Who it is for

A team watching the minutes counter

A Linux 2-core minute is USD 0.006 and a 16-core minute is USD 0.042, so the matrix build that made the suite fast is the same thing that made the invoice interesting.

A workflow that has to reach something private

The deploy step needs to talk to a database, a registry or an appliance that only accepts known addresses, and GitHub hosted runners arrive from a range too large to allowlist.

A build that needs more than the image ships

A licensed compiler, an emulator, a 30 GB fixture set, or simply more disk than the hosted runner has - and installing it again on every job is most of the pipeline.

What changes when the runner is yours

  • The workspace and the caches persist between jobs, so dependency restore stops being the longest step in the workflow.
  • One static IPv4 from AS204057 that your database, registry or VPN can allowlist by address.
  • Whatever you install stays installed - toolchains, licensed SDKs, emulators, large fixture sets, anything the hosted image does not carry.
  • Runners registered at repository, organization or enterprise level, with labels you choose and a workflow that selects them by runs-on.
  • Disk and RAM you pick, rather than the fixed shape a hosted runner comes in.
  • A machine in Prague or Covilha under a Cyprus contract, which is a data-residency answer rather than a region setting.

Sized by what the pipeline actually does

One runner service is one job at a time, so concurrency here is a count of runner processes rather than a plan tier - several on one host, each in its own directory, is the normal shape. Size from the heaviest job rather than the average, and leave disk headroom: the workspace, the tool cache and the container layers all live on it, and that persistence is the reason to self-host in the first place. Note the honest half of the trade: a flat monthly host costs the same on a week nobody pushes, where hosted minutes cost nothing. It pays off once the pipeline runs most days.

A first runner
16.49
per month

Order Now
one or two runner services
2 vCPU
8 GB RAM
80 GB NVMe
1 static IPv4
1 weekly backup included
A team runner
32.99
per month

Order Now
four to six runner services
4 vCPU
16 GB RAM
160 GB NVMe
1 static IPv4
1 weekly backup included
A busy pool
62.94
per month

Order Now
a matrix build, or several repositories
8 vCPU
32 GB RAM
240 GB NVMe
1 static IPv4
1 weekly backup included

How to install a GitHub Actions runner on a fresh host

Linux x64, in the order GitHub documents it. The registration token is generated per runner and expires an hour after you open the page, so fetch it last.

  1. Create an unprivileged user for it

    sudo adduser --disabled-password --gecos "" runner
    sudo -iu runner

    The runner executes whatever a workflow tells it to, so it should not be root and it should not share a home with anything else. Everything below runs as this user.

  2. Download and unpack the runner

    mkdir actions-runner && cd actions-runner
    curl -O -L https://github.com/actions/runner/releases/download/v2.336.0/actions-runner-linux-x64-2.336.0.tar.gz
    echo "04cf0be1aff4c3ec3554466c39124ca250e3effd8873bb7e8d68535aa9505d5d  actions-runner-linux-x64-2.336.0.tar.gz" | shasum -a 256 -c
    tar xzf ./actions-runner-linux-x64-2.336.0.tar.gz

    That version and that checksum are the ones published with release v2.336.0. Check the release page for a newer one before you copy this: the runner self-updates afterwards, but the first download is yours to verify.

  3. Configure it against a repository or an organization

    ./config.sh --url https://github.com/YOUR-ORG/YOUR-REPO --token AXXXXXXXXXXXXXXXXXXXXXXXXX --labels self-hosted,linux,x64,eu

    Take the token from Settings, then Actions, then Runners, then New self-hosted runner - it is time-limited and expires one hour after it is issued. Point the URL at the organization instead of the repository to share one runner across repositories.

  4. Run it as a service

    sudo ./svc.sh install runner
    sudo ./svc.sh start
    sudo ./svc.sh status

    svc.sh takes the user the service should run as, which is why the runner user was created first. Use ./run.sh instead if you only want to watch one job land before committing to a service.

  5. Stop needrestart from killing jobs mid-run

    echo '$nrconf{override_rc}{qr(^actions\.runner\..+\.service$)} = 0;' | sudo tee /etc/needrestart/conf.d/actions_runner_services.conf

    On Debian and Ubuntu, needrestart restarts services whose libraries were updated - including a runner in the middle of a job. GitHub documents this exact file as the way to exempt it.

  6. Point a workflow at it

    name: smoke
    on: [push]
    jobs:
      build:
        runs-on: [self-hosted, linux, x64, eu]
        steps:
          - uses: actions/checkout@v6
          - run: cat /etc/os-release

    runs-on matches on every label in the list, so a job asking for four labels only lands on a runner carrying all four. If it stays queued, one label is missing - and a job queued for more than 24 hours fails.

Checked against the official documentation on 2026-08-15 — read the source

What we are not

  • We are not GitHub. GitHub, GitHub Actions and the runner are theirs, and nothing on this page is endorsed by them. We sell the machine the runner runs on.
  • This does not replace your GitHub plan. Self-hosted runners cost no Actions minutes, which is the point, but seats, storage and everything else stay on their invoice.
  • We do not manage the runner unless you ask. You install it, or you add our administration service and we keep it patched, registered and watched.
  • Self-hosted runners are documented as a poor fit for public repositories: a fork can propose a workflow change and run it on your machine. Keep them on private repositories unless you have thought that through.
  • There is no autoscaling control plane here. If you want runners created and destroyed per job, that is Actions Runner Controller on a Kubernetes cluster - which we will happily host, on a different page.

The parts teams discover late

  • One runner service runs one job at a time. Concurrency comes from installing several, each in its own directory with its own service, not from a setting.
  • The registration token expires one hour after it is issued, so generate it when you are ready to run config.sh rather than at the start of the afternoon.
  • Labels are the whole routing mechanism. runs-on matches every label in the list, so a typo does not error - the job simply waits, and fails after 24 hours in the queue.
  • The runner needs lttng-ust, OpenSSL, Kerberos, zlib and libicu present. Debian and Ubuntu carry them; a minimal container image often does not.
  • x64 and ARM64 are supported on Linux, and ARM32 on Linux only. Debian 10 and later and Ubuntu 20.04 and later are the documented baselines.
  • Anything the workflow leaves behind stays on the disk - that is the feature, and it is also why a self-hosted runner wants a scheduled clean-up job and a snapshot.

Hosted CI minutes versus a runner of your own

Provider-hosted CI minutesA runner on DCXV
What you pay forEvery minute every job runs, for as long as the project existsA flat monthly host, whatever the pipeline does that month
A build that gets slowerCosts more every month it stays slowCosts nothing more - the machine is already paid for
Docker layer cacheCold on each job unless you upload and download it yourselfWarm on local NVMe, between jobs and between days
ConcurrencyA plan tier you upgradeA number you set in your own configuration file
Where the checkout landsA shared fleet, often in a region you cannot pinOne machine in Prague or Covilha, under a Cyprus contract
Outbound addressA large shared range that nothing can allowlistOne static IPv4 from AS204057, yours to allowlist
What the machine can holdWhatever the runner image happens to ship withAny toolchain, licence or fixture set you install once

How this gets going on your host

1

Say what the pipeline does

The heaviest job you run today, how many you want running at once, and whether it builds container images. That is enough to size a host without guessing.

2

We hand over the machine

Prague or Covilha, root access and a static IPv4, in under ten minutes. Bring your own image if the runner is already baked into one.

3

Follow the walkthrough on this page

It is the vendor sequence, taken from their current documentation rather than from a blog post, and it takes a few minutes on a clean host.

4

Snapshot it, then add the second one

Take a snapshot once the first pipeline is green, so the next machine is a restore instead of a rebuild. A pool grows by adding hosts, not by making one enormous.

Why choose us

  • Tier III certified facilities, 99.982% facility SLA
  • Own network, AS204057, IPv4 and IPv6 dual-stack
  • 24/7/365 support with ~10 minute average response
  • Cyprus company, EU jurisdiction, GDPR-native since 2007

FAQ

- How much do GitHub hosted runners cost compared with self-hosting?

GitHub bills hosted Linux runners per minute: USD 0.006 for 2-core, 0.012 for 4-core, 0.022 for 8-core and 0.042 for 16-core, with Windows at 0.010 for 2-core and macOS at 0.062 for a 3 or 4-core machine. Self-hosted runners consume no Actions minutes. A host here starts at EUR 16.49 a month, which is roughly what 2,700 minutes on a 2-core hosted runner costs - so the crossover is around 45 hours of build time a month

- How do I set up a self-hosted GitHub Actions runner?

Create an unprivileged user, download the runner tarball for linux-x64 from the actions/runner releases and verify its checksum, run ./config.sh with your repository or organization URL and the registration token from Settings then Actions then Runners, then install it as a service with sudo ./svc.sh install and start it. The exact commands are on this page. The registration token expires one hour after it is issued

- How many jobs can one runner run at once?

One. A runner service takes a single job at a time, so concurrency means installing several runner services on the host, each in its own directory. That is why the sizing on this page counts runner services rather than jobs, and why one 8 vCPU machine can comfortably carry a small matrix build

- Which operating systems and architectures are supported?

On Linux, Debian 10 and later, Ubuntu 20.04 and later, RHEL 8 and later, CentOS 8 and later, Fedora 29 and later, openSUSE 15.2 and later and their relatives, on x64, ARM64 and ARM32. The runner also needs lttng-ust, OpenSSL, Kerberos, zlib and libicu present, which Debian and Ubuntu carry as standard

- Is it safe to use a self-hosted runner on a public repository?

GitHub advises against it, and the reason is concrete: anyone can open a pull request that changes the workflow file, and that changed workflow would run on your machine. Keep self-hosted runners on private repositories unless you have read that through and put real isolation in place. Everything a job leaves behind also persists on the disk, which is a feature for caching and a risk for secrets

- Can one runner serve several repositories?

Yes. Register it at the organization level rather than the repository level and every repository in the organization can select it, using labels in runs-on to route work. A job that asks for a label no runner carries simply waits, and fails after 24 hours in the queue - which is what a typo in runs-on looks like

If you require assistance or have additional questions, please contact the managers or write to the support team at support@dcxv.com

Ready to get started?

Monthly billing, no setup fee, no lock-in

Cloud servers from €15/mo