01Services02Process03Projects04About05FAQ06Blog07Hire Me

25+ products shipped · $3.8M+ raised by clients

Back to Blog
InfrastructurePart 4 of 9September 28, 202611 min read

Creating the free server without paying for it

Subhankar Denria

Subhankar Denria

Software Architect · Product Engineer

~13 min

What this part does

Create the one always-on computer using only the settings the free tier covers, put it on the IPv6 network from post 3, log in to it from the browser — and fix the one thing Ubuntu 26.04 gets wrong on Google Cloud.

Time
One form, read carefully
Saves
At least ~$28 a month in pre-filled defaults
If you skip a step
A paid machine, a paid disk, and a sudo that says no

Step 1 — The form, field by field

☰ → Compute Engine → VM instances → Create instance.

Here's the uncomfortable truth about this form: it came up pre-filled with six settings that would have cost money or broken the free setup. Change every one of these:

The create form, as it arrived

Pre-filled settings that would cost money or break the setup

0/6 fixed

Machine type

e2-medium

~$25 / month

Image

Debian 13

the wrong OS for this setup

Disk

Balanced · 10 GB

billed

Backups

Daily snapshot schedule

billed

External IPv4

Ephemeral

~$3.65 / month

Ops Agent

Ticked

billed logs + memory

Every one of these came pre-filled. The two with a price add up to about $28.65 a month on their own — before the disk, snapshots and logs.

Machine

Field
Name
Set to
lampsill-api

Field
Region / Zone
Set to
us-central1 / us-central1-a
Why
A free region, and the same region as the network

Field
Series / Type
Set to
E2 / e2-micro
The default was
⚠️ e2-medium (~$25/month)
Why
The only free machine

Field
Provisioning model
Set to
Standard
Why
The free tier covers only normal machines. A "Spot" machine is cheaper but Google can stop it at any time

OS and storage

Field
Image
Set to
Ubuntu 26.04 LTS, x86/64 (not Arm)
The default was
⚠️ Debian 13
Why
See "Why Ubuntu 26.04" below

Field
Disk type / size
Set to
Standard persistent disk / 30 GB
The default was
⚠️ Balanced, 10 GB
Why
Standard is the only free disk type, up to 30 GB. Balanced is billed

Data protection

Field
Backups
Set to
No backups
The default was
⚠️ a daily snapshot schedule
Why
Disk snapshots are billed beyond a small allowance. The database gets its own free, encrypted backup instead (post 8)

Networking

Field
Allow HTTP / HTTPS traffic
Set to
Unticked
Why
Those open ports; we need none

Field
Network interface
Set to
Edit the existing one (open it with its ⌄ arrow). Don't add a second
The default was
⚠️ default network, with a paid IPv4

Field
→ Network / Subnet
Set to
lampsill-net / lampsill-us
Why
Where the free IPv6 comes from

Field
→ IP stack type
Set to
IPv4 and IPv6 (dual-stack)

Field
→ External IPv4 address
Set to
None
The default was
Ephemeral
Why
~$3.65/month otherwise

Field
→ External IPv6 address
Set to
Ephemeral (automatic)
Why
Free

Observability

Field
Install Ops Agent
Set to
Unticked
The default was
⚠️ Ticked
Why
Google's monitoring agent: billed logs, and memory the 1 GB machine can't spare

Leave security and advanced options at their defaults.

A small scare: after changing the image, the sidebar still said "Debian" for a moment. Open the section to check what's actually selected.

The price estimate says $6.11 — is that right?

On the right, the form shows a monthly estimate. With every setting above correct, mine said $6.11 for the machine and $0.00 for the 30 GB standard disk.

That $6.11 is the machine's list price, and it's expected. The disk's free allowance shows up in the estimate; the machine's doesn't, because its free allowance is counted in hours per month across your whole account and applied on the bill. The proof arrives a day later in Billing → Reports: the machine's usage, a matching "Free tier" credit, and a net of zero.

What should worry you in the estimate: a balanced disk, a snapshot schedule, or logging and monitoring charges. Any of those means a row above didn't stick.

Why Ubuntu 26.04 and not 24.04?

Both are long-term-support releases. 26.04 gets security updates until 2031 (24.04 until 2029), and it ships PHP 8.5 and Postgres 18 in Ubuntu's own packages — so their security fixes arrive through normal updates. On 24.04, a recent PHP has to come from an outside source.

The catch with brand-new releases is that things haven't been tested against them yet — and I hit exactly one of those (step 3). My setup scripts handle both versions, and I tested them on both before touching the real server.

→ Create. After a minute, the VM list shows lampsill-api with a green tick, an internal IP of 10.10.0.2, and an external IP starting with 2600:1900: — an IPv6 address, which is the point.

Step 2 — Log in from the browser

VM list → SSH next to lampsill-api. Say yes if it asks to authorise, or to connect "through Cloud IAP" — that's the relay from post 3.

A terminal opens in a new window. The welcome text shows the Ubuntu version, the disk (~28 GB usable) and both addresses. "The list of available updates is more than a week old" is normal on a fresh image.

Check the internet works over IPv6 before installing anything:

bash
curl -6 -sS -o /dev/null -w '%{http_code}\n' https://security.ubuntu.com

Any number — 200, 301 — means it works. An error means the IPv6 settings from post 3 or the network interface above didn't take.

Step 3 — When sudo says "I'm sorry, I'm afraid I can't do that"

On a normal Linux server, you type sudo in front of a command to run it as the administrator (root). On my brand-new Ubuntu 26.04 server:

text
$ sudo whoami
sudo: I'm sorry your_login_name. I'm afraid I can't do that

Without sudo, nothing can be installed. Here's what was going on — it's a nice example of two reasonable systems not quite fitting together.

The diagnosis

  • Google's side: when you log in, Google's agent creates your user and adds it to a group called google-sudoers. A rule file says "members of google-sudoers may use sudo". Running id confirmed I was in the group.
  • Ubuntu 26.04's side: it replaced the classic sudo program with sudo-rs, a rewrite in the Rust language. The version shipped (0.2.13) only trusts a group's member list written in the file /etc/group.
  • The gap: Google gives your user the group in a way that doesn't write your name into that member list. The line reads google-sudoers:x:1001: — empty after the last colon. Classic sudo didn't mind. sudo-rs does.
One line in /etc/group

What Google's login agent leaves behind

google-sudoers:x:1001:

The member list — after the last colon — is empty.

Classic sudo

Sees you're in the group anyway. Lets you in. (Ubuntu 24.04.)

sudo-rs

Reads only the empty list. "I'm afraid I can't do that."

After the startup script runs, as root, at boot

google-sudoers:x:1001:your_login_name

# plus /etc/sudoers.d/90-admin, checked with visudo first

$ sudo -n whoami root

Classic sudo also trusts a group given some other way. sudo-rs 0.2.13, new in Ubuntu 26.04, only reads the member list written in the file.

I reproduced it in a clean Ubuntu 26.04 container before changing anything on the real server. Ubuntu 24.04 doesn't have the problem.

The fix: a startup script

Fixing it needs root once — and without sudo, the way to get root is a startup script: a few lines Google runs as root every time the machine boots. It's Google's own documented recovery route.

Compute Engine → VM instances → lampsill-api → Edit → Automation → Startup script, and paste:

bash
#!/bin/bash
# Ubuntu 26.04 on Google Cloud: give the SSH user sudo. Ubuntu's new sudo
# (sudo-rs) only reads a group's member list, and Google leaves
# google-sudoers' empty. Safe to run on every boot.
U=your_login_name
id "$U" >/dev/null 2>&1 || exit 0
gpasswd -a "$U" google-sudoers >/dev/null 2>&1 || true
printf '%s ALL=(ALL:ALL) NOPASSWD:ALL\n' "$U" > /tmp/admin-sudo
visudo -cf /tmp/admin-sudo >/dev/null && install -m 0440 -o root -g root /tmp/admin-sudo /etc/sudoers.d/90-admin
rm -f /tmp/admin-sudo

your_login_name is the name in your SSH prompt — usually the part of your Gmail address before the @, with dots turned into underscores.

What the script does:

  1. 1Adds your user to the group's member list, which is what sudo-rs reads.
  2. 2Writes a rule that names your user directly, as a second guarantee.
  3. 3Checks the rule with visudo before installing it. A broken rule file can lock everyone out of sudo, so it's never installed unchecked.

Put it in Automation → Startup script, not Metadata. I first tried adding it as a metadata item called startup-script, which is how older guides do it. The console refused: "For adding a startup script use 'Startup script' section".

Save, then Reset at the top of the VM's page (a restart — the disk and files are untouched). SSH in again and check:

bash
sudo -n whoami

It should print root.

Leave the script in place. It does nothing when everything is right, and it repairs things if Google's agent ever rewrites the group. If you rebuild the server, paste it while creating the VM and sudo works from the first login.

What went wrong (or nearly did)

  • Six wrong defaults on the create form: a paid machine, a different operating system, a paid disk, a snapshot schedule, a paid IPv4 address and a monitoring agent. Every one was pre-filled.
  • The $6.11 estimate made me doubt everything. It's the list price; the free-tier credit appears on the bill, not the form.
  • sudo refused on Ubuntu 26.04 — a new-release rough edge between Google's login agent and Ubuntu's rewritten sudo.

What you should see

  • The VM running, with an external address starting 2600:1900: and no IPv4 external address.
  • curl -6 … from the server printing a status code.
  • sudo -n whoami printing root.

Let's connect

Choose your preferred way

Available for new projects