jwh

September 22, 2026

Building a small self-hosted agent factory

An old desktop died mid-setup, so I rented a €12 VPS and built a sandboxed agentic software factory on Coolify, Forgejo, and Hermes.

self-hostingcoolifyforgejoai agents

I read Jake Saunders' post on building a self-hosted agentic software factory and decided to try a smaller version myself.

The plan was an old desktop with an i7 and 16 GB of RAM. It died mid-setup, so I rented a VPS instead. That turned out easier to manage, and it gave me a cleaner security boundary.

The server

Netcup VPS:

  • 8 shared CPU cores
  • 16 GB RAM
  • 320 GB SSD
  • Debian 13
  • IPv4 and IPv6

About €12 a month. The CPU is shared, which is fine for experiments, small apps, and one CI job at a time.

The server is deliberately separate from my personal machines and data. Agents make mistakes, so I'd rather give them a disposable VPS than my Mac.

The stack

  • Coolify for deployments
  • Forgejo for Git hosting
  • Forgejo Actions for CI
  • Hermes Agent for the coding agent
  • PostgreSQL for Forgejo
  • Tailscale for private administration

Public addresses:

https://git.factory.jwhardwick.dev
https://hermes.factory.jwhardwick.dev

A wildcard DNS record points *.factory.jwhardwick.dev at the VPS, which makes adding more apps later easy.

Coolify

Coolify manages the Docker services and HTTPS certificates. It also gives me a way to deploy apps without handing the agent unrestricted SSH access.

The Coolify dashboard stays private. I reach it on port 8000 over Tailscale; the direct management ports are blocked from the public internet:

6001
6002
8000
8080

Public HTTPS for apps goes through Coolify's Traefik proxy.

Forgejo and CI

Forgejo stores the repositories. I added one Forgejo Actions runner using Docker-in-Docker.

The runner doesn't use the host Docker socket. Access to /var/run/docker.sock is effectively root on the box, so jobs run inside a separate Docker daemon:

services:
  docker-in-docker:
    image: docker:dind
    privileged: true
 
  runner:
    image: data.forgejo.org/forgejo/runner:13
    environment:
      DOCKER_HOST: tcp://docker-in-docker:2375

One CI job at a time to start. Sixteen gigabytes is enough for the current stack, but a few builds running together would eat it fast.

I tested the runner with a private repo and a small workflow:

name: smoke
 
on:
  push:
    branches: [main]
 
jobs:
  verify-runner:
    runs-on: docker
    steps:
      - name: Check the runner
        run: |
          node --version
          echo "Forgejo Actions runner works"

It ran, so the Git and CI path works.

Hermes

Hermes runs as a Coolify service with a persistent home directory and workspace. It uses OpenRouter for models, with a dedicated key that has a small spending limit instead of my main one.

Hermes has two restricted API credentials:

  • A Forgejo token for a dedicated hermes-agent user
  • A Coolify token with read, write, and deploy permissions

The Forgejo account is only a member of the factory organisation. The Coolify token can't read sensitive values and has no root permission.

Two local skills handle factory work:

forgejo-cli
coolify-api

The Coolify skill asks before changing anything — read-only calls work normally, mutations need a --yes flag, and it supports dry runs.

I tested all three parts separately:

HERMES_FACTORY_OK
FORGEJO_INTEGRATION_OK
COOLIFY_SKILL_OK

That confirmed model access, Forgejo access, and Coolify access.

Network access

Public ports:

22      SSH
80      HTTP redirects and certificate setup
443     HTTPS
22222   Forgejo Git over SSH

Coolify's management ports are blocked on the public interface; Tailscale is the private route for administration. SSH password auth is off — keys only.

Backups

Netcup snapshots are useful but I don't treat them as backups. A provider or account problem could take out the server and its snapshots together.

Off-server backup, stored on my FileVault-encrypted Mac:

  • Coolify's PostgreSQL database
  • Forgejo's PostgreSQL database
  • Forgejo repositories and configuration
  • Hermes state and workspace
  • Runner configuration
  • Coolify configuration

Current resource use

With Coolify, Forgejo, PostgreSQL, Hermes, and the runner running:

RAM:  2.1 GB of 15 GB
Disk: 19 GB of 314 GB

Plenty of room for small apps. I haven't installed Firecrawl yet — its browser workers can use a lot more memory.

What's next

The next test is to hand Hermes a small app idea and have it:

  1. Create a Forgejo repository
  2. Write the application
  3. Add tests and a CI workflow
  4. Push the code
  5. Deploy it through Coolify

This isn't meant to hold personal data or important production systems. It's a separate machine where agents can build and break things without touching the rest of my infrastructure.