Hosted CI minutes are metered; a small VPS is not. The build cache stays warm, the Docker layers stay pulled, and the machine costs the same whether you push once a week or forty times a day. There is exactly one rule you must not break.Gehostete CI-Minuten werden abgerechnet; ein kleiner VPS nicht. Der Build-Cache bleibt warm, die Docker-Layer bleiben gezogen, und die Maschine kostet gleich viel, ob du einmal die Woche pushst oder vierzig Mal am Tag. Es gibt genau eine Regel, die du nicht brechen darfst.
A self-hosted runner executes whatever the workflow file says. On a public repository, anyone can open a pull request from a fork — and if your workflow runs on pull_request, their code runs on your machine. GitHub says this in its own documentation and it is not a theoretical risk: it is how self-hosted runners get turned into crypto miners and credential dumps.Ein selbstgehosteter Runner führt aus, was auch immer in der Workflow-Datei steht. Bei einem öffentlichen Repository kann jeder einen Pull Request aus einem Fork öffnen — und wenn dein Workflow auf pull_request läuft, läuft ihr Code auf deiner Maschine. GitHub schreibt das in seiner eigenen Dokumentation, und es ist kein theoretisches Risiko: So werden selbstgehostete Runner zu Krypto-Minern und Credential-Dumps umfunktioniert.
Private repositories only, unless you are running ephemeral runners inside disposable infrastructure and you know exactly why that changes the answer. If you need CI on a public repo, use the hosted runners. That is what they are good at.Nur private Repositories, es sei denn, du betreibst ephemere Runner in Wegwerf-Infrastruktur und weißt genau, warum das die Antwort ändert. Brauchst du CI auf einem öffentlichen Repo, nutze die gehosteten Runner. Dafür sind sie gemacht.
CI is bursty CPU and steady disk. 2 vCPU and 4 GB handles typical Node, Python and Go builds without drama; Docker image builds and anything that compiles native code will use everything you give it.CI ist stoßweise CPU und gleichmäßige Festplatte. 2 vCPU und 4 GB bewältigen typische Node-, Python- und Go-Builds ohne Drama; Docker-Image-Builds und alles, was nativen Code kompiliert, nutzt alles, was du ihm gibst.
Disk is the sleeper. Between the workspace, the Docker layer cache, and the package caches, a busy runner will quietly eat tens of gigabytes. Plan to prune (below), and know how you would grow the disk before you need to — add extra storage to your VPS covers that on our machines.Die Festplatte ist der stille Kandidat. Zwischen Workspace, Docker-Layer-Cache und den Paket-Caches frisst ein vielbeschäftigter Runner still und leise zig Gigabyte. Plane das Aufräumen ein (unten), und wisse, wie du die Festplatte vergrößerst, bevor du es musst — Speicherplatz zu deinem VPS hinzufügen beschreibt das auf unseren Maschinen.
One pleasant surprise: a runner only makes outbound connections. It polls the CI service for work; nothing connects in. That means NAT IPv4 is completely fine here — no dedicated address, no port forwarding, nothing to configure. It is one of the few server workloads with no networking caveat at all (NAT IPv4 vs a dedicated IP if you want the background).Eine angenehme Überraschung: Ein Runner baut nur ausgehende Verbindungen auf. Er fragt beim CI-Dienst nach Arbeit; nichts verbindet sich herein. Das heißt, NAT-IPv4 ist hier völlig in Ordnung — keine dedizierte Adresse, keine Portweiterleitung, nichts zu konfigurieren. Es ist eine der wenigen Server-Workloads ganz ohne Netzwerk-Vorbehalt (NAT IPv4 vs. dedizierte IP, falls du den Hintergrund willst).
Deploy an Ubuntu LTS image and do the basics first: connect to your VPS over SSH and secure your VPS. Then make a user for the runner that is not root and not you:Setze ein Ubuntu-LTS-Image auf und erledige zuerst die Grundlagen: über SSH mit deinem VPS verbinden und deinen VPS absichern. Leg dann für den Runner einen Nutzer an, der nicht root und nicht du ist:
sudo adduser --disabled-password --gecos "" runner
If your jobs need Docker, adding runner to the docker group grants effectively root on the host, because it can mount the host filesystem into a container. That is an acceptable trade on a single-purpose machine that only builds your own private code. It is not acceptable on a machine that does anything else.Brauchen deine Jobs Docker, gibt das Hinzufügen von runner zur docker-Gruppe effektiv Root auf dem Host, weil er das Host-Dateisystem in einen Container mounten kann. Das ist ein akzeptabler Kompromiss auf einer Single-Purpose-Maschine, die nur deinen eigenen privaten Code baut. Es ist nicht akzeptabel auf einer Maschine, die noch irgendetwas anderes tut.
Get the download command and registration token from Settings → Actions → Runners → New self-hosted runner on the repository or organisation — the token is short-lived, so generate it when you are ready to use it.Hol dir den Download-Befehl und das Registrierungs-Token unter Settings → Actions → Runners → New self-hosted runner im Repository oder in der Organisation — das Token ist kurzlebig, erzeuge es also erst, wenn du bereit bist, es zu verwenden.
sudo -u runner -i
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64.tar.gz -L \
https://github.com/actions/runner/releases/download/v2.322.0/actions-runner-linux-x64-2.322.0.tar.gz
tar xzf actions-runner-linux-x64.tar.gz
./config.sh --url https://github.com/YOURORG/YOURREPO \
--token YOUR_REGISTRATION_TOKEN \
--labels self-hosted,linux,x64,vps \
--unattended
Install it as a service so it survives a reboot. This part runs as root because it writes a systemd unit, but the service itself runs as runner:Installiere ihn als Dienst, damit er einen Reboot übersteht. Dieser Teil läuft als root, weil er eine systemd-Unit schreibt, aber der Dienst selbst läuft als runner:
exit
cd /home/runner/actions-runner
sudo ./svc.sh install runner
sudo ./svc.sh start
Target it from a workflow with the labels you set:Sprich ihn aus einem Workflow mit den Labels an, die du gesetzt hast:
jobs:
build:
runs-on: [self-hosted, linux, vps]
A long-lived runner accumulates state: leftover files, mutated global config, a node_modules from three branches ago. Worse, one job can leave something behind that the next job picks up.Ein langlebiger Runner sammelt Zustand an: übrig gebliebene Dateien, veränderte globale Konfiguration, ein node_modules von drei Branches zuvor. Schlimmer noch: Ein Job kann etwas hinterlassen, das der nächste Job aufgreift.
./config.sh --ephemeral makes the runner take exactly one job and then deregister. Pair it with a supervisor that re-registers a fresh one and you get the isolation of hosted CI with the warmth of your own cache. It is more moving parts; it is also the difference between "the build only fails on the runner" and a machine you trust../config.sh --ephemeral lässt den Runner genau einen Job annehmen und sich dann abmelden. Kombinierst du das mit einem Supervisor, der einen frischen neu registriert, bekommst du die Isolation von gehosteter CI mit der Wärme deines eigenen Caches. Das sind mehr bewegliche Teile; es ist aber auch der Unterschied zwischen „der Build schlägt nur auf dem Runner fehl“ und einer Maschine, der du vertraust.
Same shape, different words. Install gitlab-runner, then:Gleiche Form, andere Worte. Installiere gitlab-runner, dann:
sudo gitlab-runner register \
--non-interactive \
--url https://gitlab.com/ \
--token YOUR_RUNNER_TOKEN \
--executor docker \
--docker-image alpine:latest
The docker executor is the one you want: each job gets a fresh container, so the isolation problem from step 3 is solved by default. Its cost is that Docker layer caching between jobs needs deliberate configuration rather than happening for free.Der Docker-Executor ist der, den du willst: Jeder Job bekommt einen frischen Container, sodass das Isolationsproblem aus Schritt 3 standardmäßig gelöst ist. Der Preis dafür: Docker-Layer-Caching zwischen Jobs braucht bewusste Konfiguration, statt gratis zu passieren.
This is the maintenance that actually matters. A weekly timer is enough:Das ist die Wartung, die wirklich zählt. Ein wöchentlicher Timer reicht:
docker system prune -af --filter "until=168h"
docker volume prune -f
Add a cleanup of the runner's _work directory if your jobs leave large artefacts behind, and put a disk-usage alert somewhere you will see it. A CI runner that is 100% full does not fail loudly — it fails weirdly, halfway through a step, with an error about something unrelated.Füge ein Aufräumen des _work-Verzeichnisses des Runners hinzu, wenn deine Jobs große Artefakte hinterlassen, und platziere irgendwo, wo du es siehst, einen Alarm für die Festplattenauslastung. Ein CI-Runner, der zu 100 % voll ist, scheitert nicht laut — er scheitert seltsam, mitten in einem Schritt, mit einem Fehler zu etwas völlig anderem.
The runner holds real credentials in memory while a job runs. Two habits worth having from day one: keep the machine single-purpose, so nothing else on it can read process memory or the workspace, and prefer short-lived tokens (OIDC to your cloud provider) over long-lived keys stored in repository secrets. If a token can only be exchanged for fifteen minutes of access, a compromised job is an incident rather than a catastrophe.Der Runner hält echte Credentials im Speicher, während ein Job läuft. Zwei Gewohnheiten, die sich vom ersten Tag an lohnen: Halte die Maschine Single-Purpose, damit nichts anderes darauf Prozessspeicher oder Workspace lesen kann, und bevorzuge kurzlebige Token (OIDC zu deinem Cloud-Anbieter) gegenüber langlebigen Keys in Repository-Secrets. Lässt sich ein Token nur gegen fünfzehn Minuten Zugriff eintauschen, ist ein kompromittierter Job ein Vorfall statt einer Katastrophe.
Full disclosure: this is what we sell. A runner is a steady, boring workload — two cores and a few gigabytes, up all month, which is exactly what a monthly VPS is priced for.Zur vollen Transparenz: Das ist, was wir verkaufen. Ein Runner ist eine gleichmäßige, unspektakuläre Last — zwei Kerne und ein paar Gigabyte, den ganzen Monat über an, und genau dafür ist ein monatlicher VPS kalkuliert.
Linux KVM VPS — EUR 4.99 to EUR 59.99 a month, on our own single-tenant bare metal in Dallas, TX and Charlotte, NC. Full hardware virtualisation (KVM), your own kernel, full root. Six tiers, vps-starter to vps-ultra. Starter is 1 vCPU, 1 GiB RAM, 25 GB disk.Linux-KVM-VPS — 4,99 bis 59,99 EUR im Monat, auf unserer eigenen Single-Tenant-Bare-Metal-Hardware in Dallas, TX und Charlotte, NC. Vollständige Hardware-Virtualisierung (KVM), eigener Kernel, volles Root. Sechs Tarife, vps-starter bis vps-ultra. Starter hat 1 vCPU, 1 GiB RAM, 25 GB Speicher.
You order in the shop, pay by card (Stripe) or SEPA bank transfer, and your login details are e-mailed to you once the service is set up. Support is e-mail, run by one person, with no guaranteed response time. All prices are final totals under the German small-business rule (§19 UStG); no VAT is added or shown.Du bestellst im Shop, zahlst per Karte (Stripe) oder SEPA-Überweisung, und deine Zugangsdaten werden dir per E-Mail zugeschickt, sobald der Dienst eingerichtet ist. Support läuft per E-Mail, von einer einzelnen Person betrieben, ohne garantierte Reaktionszeit. Alle Preise sind Endpreise. Gemäß § 19 UStG wird keine Umsatzsteuer ausgewiesen.
Order vps-starter → · Linux KVM VPS overviewvps-starter bestellen → · Übersicht Linux-KVM-VPS
Written by the person who runs overnight.host: a small, honest hosting company on dedicated bare metal — Linux VPS, game servers, web hosting. Live status at up.overnight.host.Geschrieben von der Person, die overnight.host betreibt: ein kleines, ehrliches Hosting-Unternehmen auf dedizierter Bare-Metal-Hardware — Linux-VPS, Gameserver, Webhosting. Live-Status unter up.overnight.host.
It stops being metered, which is the point. A machine that costs the same every month is easier to reason about than a minute counter, and the warm cache often makes builds faster on modest hardware than a cold hosted runner on fast hardware. Whether it is cheaper depends entirely on how much you build.Es hört auf, abgerechnet zu werden, und das ist der Punkt. Eine Maschine, die jeden Monat gleich viel kostet, lässt sich leichter kalkulieren als ein Minutenzähler, und der warme Cache macht Builds auf bescheidener Hardware oft schneller als ein kalter gehosteter Runner auf schneller Hardware. Ob es günstiger ist, hängt vollständig davon ab, wie viel du baust.
You should not. A pull request from a fork can run arbitrary code on your machine. Keep self-hosted runners on private repositories.Solltest du nicht. Ein Pull Request aus einem Fork kann beliebigen Code auf deiner Maschine ausführen. Halte selbstgehostete Runner auf privaten Repositories.
More than you think — the workspace plus the Docker layer cache plus package caches. Start at 40 GB or more for anything that builds containers, and prune on a schedule.Mehr, als du denkst — der Workspace plus der Docker-Layer-Cache plus die Paket-Caches. Starte bei 40 GB oder mehr für alles, was Container baut, und räume nach Zeitplan auf.
No. It only makes outbound connections, so NAT IPv4 with no forwarded ports works perfectly.Nein. Er baut nur ausgehende Verbindungen auf, sodass NAT-IPv4 ganz ohne weitergeleitete Ports einwandfrei funktioniert.
One per concurrent job you want. Two small runners on separate machines beat one large runner for throughput, because CI parallelism is mostly about not queueing.Einen pro gewünschtem gleichzeitigem Job. Zwei kleine Runner auf getrennten Maschinen schlagen beim Durchsatz einen großen Runner, weil es bei CI-Parallelität vor allem darum geht, nicht in die Warteschlange zu geraten.
Prices are final totals; no VAT is shown (§19 UStG). Need something the shop does not list? Email us for a written offer.Alle Preise sind Endpreise ohne ausgewiesene USt. (§19 UStG). Du brauchst etwas, das nicht im Shop steht? Schreib uns für ein schriftliches Angebot.
Order now →Jetzt bestellen → Request a custom configIndividuelle Konfiguration anfragen