← Back to list

Running Steve Yegge’s Beads as a Secure Multi-Developer Remote Server on GCP

Set up a secure Dolt database using GCP's Compute Engine VMs, IAP SSH tunneling & 2FA. Use Beads for a multi-developer/multi-agent setup.

Roope Paukku · 2026-05-15 13:15 · 5 claps · 13.0 min read
#software-development #agentic-coding #beads #google-compute-engine #google-cloud-platform
Open on Medium ↗
Wiki topics: AGT · AI Agents 💻 · Programming ☁️ · DevOps & Cloud 🏃 · Running & Endurance

Running Steve Yegge’s Beads as a Secure Multi-Developer Remote Server on GCP

I have begun using a multi-agent coding workflow with a pretty standard pattern: Plan → Implement → Validate → Iterate → Ship. This centers around skills as the instructors and Beads as the shared context and task management layer.

For those who don’t know, Beads is a agentic-first lightweight issue/project tracking tool that can use Dolt as its backend. Dolt gives you a “Git-for-SQL” database engine with a MySQL-compatible protocol.

Beads are incredibly easy to setup locally and I have been mostly using them privately in the--stealth mode. However, my fellow developers and their agents don’t then have access to the beads I create. I use beads to create breakdowns of major features but also some minor improvement tasks/TODOs which would be beneficial to share with the other developers so we have a single source of “programming tasks” to be done. Also, when moving into multi-consumer agentic workflows, a single, feature-branch independent source of truth for the context and the tasks is a must-have.

So, here I present how we setup a simple and lightweight remote Beads setup for a multi-developer and multi-agent workflow.

1. Target architecture

The architecture we built:

  • Database engine: Dolt
  • Application interface: Beads CLI, bd
  • Topology: centralized server model
  • Client access: GCP IAP TCP forwarding (Zero-Trust Access)
  • Network exposure: no external VM IP, no public MySQL port
  • Storage: separate persistent data disk mounted at /mnt/disks/beads-data
  • Dolt mode: --data-dir mode, so each Beads project becomes its own SQL database
  • Service management: systemd
  • Linux service user: dedicated non-login user beads-dolt

In this setup, a single Google Cloud VM runs Dolt in server mode. Every Beads project gets its own Dolt SQL database inside one shared data directory. Developers connect from their laptops through an Identity-Aware Proxy SSH tunnel. The connection is pretty much like this:

Developer laptop → bd CLI → local 127.0.0.1:13306 → authenticated IAP SSH tunnel → private GCP VM → Dolt bound to 127.0.0.1:3306 → persistent disk at /mnt/disks/beads-data

2. GCP Setup

The VM is created with a security-first posture.

VM Settings

In Compute Engine, create a VM with:

  • Standard provisioning
  • Deletion protection enabled
  • Live migration enabled
  • Shielded VM enabled
  • Secure Boot
  • vTPM
  • Integrity Monitoring
  • OS Login enabled
  • OS Login 2FA enabled if your organization supports it
  • Project-wide SSH keys blocked

We chose the e2-standard-2 machine for the demo setup.

Add a secondary data disk separate from the boot disk:

Recommended settings (use SSD or Extreme Persistent Disks for optimal performance):

Make sure the deletion rule on the data disk is “Keep disk” for data persistence (found under Add New Disk -> Advanced Configuration).

For networking settings, use the following for a private VM:

  • no public HTTP
  • no public HTTPS
  • no public MySQL 3306
  • no public SSH from 0.0.0.0/0

SetExternal IPv4 -> None under “Network interfaces”:

For security, I recommend turning all “Shielded VM” features on. Also allow access to all Cloud APIs.

For VM access, we chose to use gcloud with 2FA instead of SSH keys for ease of use and security (Zero Trust Access). The rest of tutorial is built around this choice, so bear that in mind. If you decide to use SSH keys yourself, the exact commands you should use later might be different (primarily for setting up the tunnel), but you can still keep following the tutorial.

With these settings, developers/admins will need permissions for:

  • OS Login SSH access to the VM
  • IAP TCP forwarding

Typical roles include, depending on your org policy:

  • roles/compute.osLogin or roles/compute.osAdminLogin
  • roles/iap.tunnelResourceAccessor

These settings can be applied for principals at “IAM & Admin → IAM → Allow → Grant Access”

Coming back to the VM creation, in the Advanced settings tab, we chose to “Enable deletion protection” to ensure nobody deletes this VM accidentally.

Now, go ahead and Create your instance.

After the creation, for IAP SSH access, ensure there is a firewall rule that allows SSH from Google’s IAP TCP forwarding range:

35.235.240.0/20 -> TCP:22

This allows gcloud compute ssh --tunnel-through-iap to work without giving the VM a public IP. You can add this rule at “Network Security → Firewall Policies → Create firewall rule”

Choose a name and add Google’s IAP TCP forwarding range:

Allow TCP traffic and Create the rule:

Now you should be able to login to your instance via:

gcloud auth login

gcloud compute ssh <vm-instance> --project=<gcp-project> --zone=<vm-zone> --tunnel-through-iap

(remember to set the IAM policies)

The next steps are done on the VM unless stated otherwise. So login first!

3. Storage Setup

The VM uses two disks:

  • boot disk for the operating system
  • secondary persistent disk for Beads/Dolt data

Check what is the data disk (run on the VM): lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS,MODEL

In our case it looked like this:

And the stable GCP disk symlinks were (ls -l /dev/disk/by-id/ | grep google):

So the data disk was dev/disk/by-id/google-beads-disk .

Format and Mount the Data Disk

On the VM, set the data disk variable: export DATA_DEV=/dev/disk/by-id/google-beads-disk

Check that the disk has no existing filesystem signatures:sudo wipefs -n "$DATA_DEV"

Format it as ext4: sudo mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard "$DATA_DEV"

Create the mount point:sudo mkdir -p /mnt/disks/beads-data

Mount it:sudo mount -o discard,defaults "$DATA_DEV" /mnt/disks/beads-data

Verify the mount:df -hT /mnt/disks/beads-data

Also inspect block devices again:lsblk -f

Persist the mount in /etc/fstab:UUID="$(sudo blkid -s UUID -o value "$DATA_DEV")"

Then append the mount entry:echo "UUID=$UUID /mnt/disks/beads-data ext4 discard,defaults,nofail 0 2" | sudo tee -a /etc/fstab

Reload systemd and verify the mount configuration:

sudo systemctl daemon-reload

sudo findmnt --verify

Test remounting:

sudo umount /mnt/disks/beads-data

sudo mount -a

df -hT /mnt/disks/beads-data

Troubleshooting: Boot Disk and Data Disk May Be Reversed

Do not assume the boot disk is always /dev/sda. In our case:

  • /dev/sdb was the boot disk
  • /dev/sda was the data disk

Always verify with lsblk -f and ls -l /dev/disk/by-id/ | grep google .

Use the stable /dev/disk/by-id/google-... path rather than guessing /dev/sda or /dev/sdb.

4. Add Cloud NAT for Outbound Internet

Because the VM we configured had no external IP (outbound internet path), apt package installations failed. Since we want a secure deployment, the fix is not to add an external IP. The better solution is Cloud NAT.

Cloud NAT gives the private VM outbound internet access while preserving:

  • no external VM IP
  • no public SSH
  • no public database port
  • IAP-only administrative access

Discover Network and Subnet

From a local machine (not the VM) with gcloud, run:

gcloud compute instances describe <instance> --project=<project> --zone=<vm-zone> --format="get(networkInterfaces[0].network,networkInterfaces[0].subnetwork)"

The result shows your:

  • project
  • region
  • zone
  • network
  • subnet

Create Cloud Router and NAT

Create a Cloud Router if it doesn’t exist in your project:

gcloud compute routers create beads-nat-router --project=<project> --region=<region> --network=default

Create Cloud NAT:

gcloud compute routers nats create beads-nat --project=<project> --router=beads-nat-router --region=<region> --nat-all-subnet-ip-ranges --auto-allocate-nat-external-ips

This gives private VMs in the default network outbound internet access through NAT. Inbound access remains blocked except through IAP/Google-controlled paths. This setup permits all VMs in the project/zone for outbound traffic, so be sure to verify if this appropriate in your context.

Verify the NAT:

gcloud compute routers nats describe beads-nat --project=<project> --router=beads-nat-router --region=<region>

Once NAT was active, the required package downloads and GitHub downloads should work.

Troubleshooting: No Cloud NAT

If installing Dolt or Debian packages fails with network timeouts, check whether the VM has outbound internet. Cloud NAT is my recommended approach to allow outbound connectivity. There are some other ways to install Beads and Dolt to the VM without Cloud NAT, such as copying the installation files via the tunnel we have created.

5. Install Dolt

On the VM, update package lists:

sudo apt-get update

Install basic tools:

sudo apt-get install -y curl ca-certificates default-mysql-client

Download the Dolt installer:

curl -fsSL https://github.com/dolthub/dolt/releases/latest/download/install.sh -o /tmp/dolt-install.sh

Run the installer:

sudo bash /tmp/dolt-install.sh

Verify the installation:

which dolt

dolt version

In our case, Dolt installed successfully as:

/usr/local/bin/dolt

with version:

dolt version 2.0.3

6. Create a Dedicated Dolt Service User

It is not recommended to run Dolt as root for obvious reasons. Rather, let’s create a system user:

sudo useradd --system --home-dir /var/lib/beads-dolt --create-home --shell /usr/sbin/nologin beads-dolt

Give it ownership of the data directory and its home directory:

sudo chown -R beads-dolt:beads-dolt /mnt/disks/beads-data

sudo chown -R beads-dolt:beads-dolt /var/lib/beads-dolt

Configure Dolt identity for commits and metadata:

sudo -u beads-dolt HOME=/var/lib/beads-dolt dolt config --global --add user.name "Beads Dolt Server"

sudo -u beads-dolt HOME=/var/lib/beads-dolt dolt config --global --add user.email "beads-dolt@localhost"

Verify the config:

sudo -u beads-dolt HOME=/var/lib/beads-dolt dolt config --global --list

7. Create the Dolt systemd Service

Create the service file:

sudo nano /etc/systemd/system/beads-dolt.service

Paste this service definition:

[Unit]

Description=Beads Dolt SQL Server

Documentation=https://github.com/dolthub/dolt

After=network-online.target

Wants=network-online.target

RequiresMountsFor=/mnt/disks/beads-data

[Service]

Type=simple

User=beads-dolt

Group=beads-dolt

Environment=HOME=/var/lib/beads-dolt

WorkingDirectory=/mnt/disks/beads-data

ExecStart=/usr/local/bin/dolt sql-server --data-dir /mnt/disks/beads-data --host 127.0.0.1 --port 3306

Restart=on-failure

RestartSec=5

LimitNOFILE=65535

NoNewPrivileges=true

PrivateTmp=true

ProtectSystem=full

ProtectHome=true

ReadWritePaths=/mnt/disks/beads-data /var/lib/beads-dolt

[Install]

WantedBy=multi-user.target

Reload systemd:sudo systemctl daemon-reload

Enable and start the service:sudo systemctl enable --now beads-dolt

Check service status:sudo systemctl status beads-dolt --no-pager

Check recent logs:sudo journalctl -u beads-dolt -n 100 --no-pager

Confirm Dolt is listening only on loopback:sudo ss -ltnp | grep 3306

This is the important safety check. You want Dolt bound to 127.0.0.1:3306, not 0.0.0.0:3306.

8. Test Dolt Locally on the VM

Run:mysql -h 127.0.0.1 -P 3306 -u root -e "SHOW DATABASES;"

Before creating any Beads databases, the expected databases are:

This confirms Dolt SQL server is working locally.

9. Open an IAP Tunnel From the Developer Machine

From the local developer machine (not the VM), run:

gcloud compute ssh <instance> --project=<project --zone=<vm-zone> --tunnel-through-iap -- -N -L 127.0.0.1:13306:127.0.0.1:3306

Leave this terminal open. This maps:

local 127.0.0.1:13306 -> IAP SSH tunnel -> VM 127.0.0.1:3306

I use local port 13306 rather than 3306 to avoid conflicts with a local MySQL or MariaDB server.

Optionally test from the local machine:mysql -h 127.0.0.1 -P 13306 -u root -e "SHOW DATABASES;"

10. Initialize a Remote Beads Project

On the local developer machine again, create a project directory:

mkdir -p ~/remote-beads-test && cd ~/remote-beads-test

Assuming you have Beads installed, initialize Beads against the remote Dolt server (replace remote_beads_test with your database name if you want to):

bd init --server --external --database remote_beads_test --server-host 127.0.0.1 --server-port 13306

I recommend setting auto-export to “n”, we experienced some version control hiccups if it was set to “y”.

On the VM, verify the database exists:

mysql -h 127.0.0.1 -P 3306 -u root -e "SHOW DATABASES;"

The database list should now include remote_beads_test or whatever you named your database as:

Also verify the directory exists:sudo ls -la /mnt/disks/beads-data

You should see your database name there(remote_beads_test in our case):

11. Test Beads

First, with the tunnel running, run bd status on the local machine:

Create a test issue:

bd create "Test issue for remote Beads"

bd list :

bd show <issue> :

As we can see, the remote server works! The issue has the correct owner as well.

You may stop here, or read along for some recommended security tightenings.

Troubleshooting

Make sure all Beads users have up-to-date versions of Beads and Dolt installed. Make sure the server also has an up-to-date version of Dolt. Version mismatches might cause issues when connecting to the database and when managing issues.

(12.) Tighten Data Directory Permissions

After Dolt generated its config, we can tighten permissions.

On the VM:

sudo chmod 600 /mnt/disks/beads-data/config.yaml

sudo chown beads-dolt:beads-dolt /mnt/disks/beads-data/config.yaml

sudo chmod 750 /mnt/disks/beads-data

Verify:

sudo ls -la /mnt/disks/beads-data

The final shape should include:

  • /mnt/disks/beads-data owned by beads-dolt:beads-dolt
  • config.yaml owned by beads-dolt:beads-dolt
  • config.yaml with mode 600
  • project directories such as remote_beads_test

Restart Dolt:

sudo systemctl restart beads-dolt

Check status:

sudo systemctl status beads-dolt --no-pager

Verify the database is still visible:

mysql -h 127.0.0.1 -P 3306 -u root -e "SHOW DATABASES;"

Troubleshooting: Permission denied When Listing the Data Directory

After running:

sudo chmod 750 /mnt/disks/beads-data

a normal SSH user may get:

ls: cannot open directory '/mnt/disks/beads-data': Permission denied

That is expected. The directory belongs to beads-dolt.

Use:

sudo ls -la /mnt/disks/beads-data

The Dolt service still works because it runs as beads-dolt.

About the secure_file_priv Warning

Dolt logged this warning:

secure_file_priv is set to "", which is insecure.

It also warned:

Any user with GRANT FILE privileges will be able to read any file which the sql-server process can read.

This warning did not block functionality.

We checked whether Dolt 2.0.3 exposed a direct flag:

dolt sql-server --help | grep -i secure

The output showed TLS-related settings, but not a direct secure-file-priv option:

listener.require_secure_transport

listener.tls_cert

listener.tls_key

So we left the service as-is.

This is acceptable for the current architecture because:

  • Dolt runs as beads-dolt, not root
  • Dolt listens only on 127.0.0.1
  • the VM has no external IP
  • developers must have IAM/IAP/SSH access before reaching SQL
  • the data directory permissions are restricted

Verify no accidental systemd override was active:

sudo systemctl cat beads-dolt

The clean active command was:

ExecStart=/usr/local/bin/dolt sql-server --data-dir /mnt/disks/beads-data --host 127.0.0.1 --port 3306

13. Final Verification

On the VM, check service status using sudo systemctl status beads-dolt --no-pager :

Check the listener sudo ss -ltnp | grep 3306 :

Inspect the data directory sudo ls -la /mnt/disks/beads-data :

List databases mysql -h 127.0.0.1 -P 3306 -u root -e "SHOW DATABASES;" :

14. Conveniences

After the setup, we created a shareable tunneling script for developers to use when wanting to connect to the remote Beads database. I can’t share the script directly since it contains a few confidential details like our project ids and instance ids, but I believe you can scrape a script together from the snippets in this article quite easily.

The daily developer workflow is now something like this:

  1. Start the tunnel:

beads-tunnel

  1. In another terminal, enter the Beads project:

cd /path/to/beads/project

3.a. If it is an existing project, use the Beads commands directly.

bd status

3.b. For a new project/database, create a directory:

mkdir -p ~/projects/my-new-beads-project && cd ~/projects/my-new-beads-project

Initialize Beads:

bd init --server --external --database my_new_beads_project --server-host 127.0.0.1 --server-port 13306

3.c. For a locally new project but with an existing Beads database (e.g. new developer joining):

  • (Git clone)
  • If the repo already contains the Beads metadata/config and you have the tunnel open, you can usually go straight to bd status
  • If the Beads metadata is corrupted/missing (e.g. gitignored) or you just want to use Beads outside of a specific project for collaboration, you can use bd init --server --external --database remote_beads_project --server-host 127.0.0.1 --server-port 13306 (IMPORTANT: the database name should match the remote database name)
  • If not in a tracked repository, when Beads asks Contributing to someone else's repo? [y/N]: the new developer should answern .
  • That’s it!

15. Recommended Next Hardening Steps

  1. Add a snapshot schedule for google-beads-disk.
  2. Restrict IAP and OS Login IAM to a specific developer group.
  3. Document Beads database naming conventions.
  4. Monitor disk usage with df -hT /mnt/disks/beads-data.
  5. Keep OS packages updated with sudo apt-get update and sudo apt-get upgrade.

Conclusion

Thank you for reading through! I hope this article provided enough detail into how we provisioned a Beads server using GCP so you can do the same in your context.

In summary:

  • Provision a VM with a persistent (and fast) disk
  • Define your networking and access protocol to your security preferences (we used GCP’s IAP tunneling via gcloud and 2FA)
  • Ensure external connections are available for the VM (via e.g. Cloud NAT) for package installations
  • Install and run Dolt (we used systemd with a specific user role)
  • Create a tunnel to your VM (gcloud compute ssh)
  • Point Beads to your tunnel (bd init --server --external --database remote_beads_project --server-host 127.0.0.1 --server-port 13306 )

I’m sure there are plenty of optimizations to be made here, but this simple setup was enough for us. You could utilize Cloud Run for more cost-effectiviness for example. I’ll leave these up to you :)

Happy vibing! -R


메타데이터
post_id
5268f4dc2ddc
slug
running-steve-yegges-beads-as-a-secure-multi-developer-remote-server-on-gcp-5268f4dc2ddc
url
https://medium.com/@roope.paukku/running-steve-yegges-beads-as-a-secure-multi-developer-remote-server-on-gcp-5268f4dc2ddc
canonical_url
https://medium.com/@roope.paukku/running-steve-yegges-beads-as-a-secure-multi-developer-remote-server-on-gcp-5268f4dc2ddc
author_url
https://medium.com/@roope.paukku
status
ok
fetched_at
2026-06-09 15:37:30