OpenCode: The Model-Agnostic AI Coding CLI I’ve Relied On for a Year

I have been using AI coding assistants for a while now. They all do roughly the same thing: help you write and understand code faster. But the one I keep coming back to is OpenCode. It fits like a glove.

Built for the terminal

OpenCode follows what good CLI tools do: lightweight, minimal interface, but gives you exactly what you need to see what is going on. The output is colour-coded, well laid out, and easy to read at a glance. When it explains what it is doing, I can see the plan clearly before it executes. When it writes code, I can review the diff before applying it.

OpenCode CLI interface showing structured AI response about WordPress wp-login.php

Desktop-based applications often either focus on the wrong things, or there is too much going on to keep the important functionalities in mind. OpenCode feels like a tool designed for people who live in the terminal.

Open.. everything

The OpenCode CLI is open source but also open, full stop. It does not try to lock you into anything. It tries to support as many different platforms, subscriptions etc. I want the flexibility to switch AI provider when there is a deal, a new model or to walk away from something I no longer like. Open source also means the community can contribute, audit, and improve it.

The OpenCode Go subscription

This is not needed to use OpenCode if you already have a subscription. If you don’t and/or you’ve been sitting on the fence for quite some time, maybe waiting for the perfect entry point into AI, I suggest you give it a try.

OpenCode Go costs then $10 per month ($5 for the first month). For this $10 you get $60 worth of usage. This is because usage is measured in dollar value rather than raw request counts.

The limits run in rolling windows to avoid overloading the platform. You get a 5-hour rolling limit of $12, a weekly limit of $30, and a monthly limit of $60. The actual number of requests you can make depends on which model you use — cheaper models get you many more requests, advanced ones fewer.

For example, with DeepSeek V4 Flash I get over 30,000 requests per 5-hour window. That is plenty for daily use. More advanced models like GLM cost more per request, so the same window buys fewer of them — I save those for when I need stronger reasoning.

If I need more usage, I can top up credit or let it fall back to my Zen balance. And once I hit the paid limits, the free models included with OpenCode still work.

There is also OpenCode Zen if you want the flexibility of a wider range of models and providers. But for most of what I do, Go covers it.

Models I actually use

I have settled on a few that cover most of my needs:

DeepSeek V4 Flash — my everyday driver. Fast, great value on credits, good for probably 80% of my interactions.

MiMo V2.5 — when I need vision capabilities. Looking at screenshots, debugging UI issues. Similar value to DeepSeek Flash.

GLM 5.x — for infrastructure work and complex multi-step reasoning. Excellent at planning, but I use it selectively because it consumes credits faster.

The ability to pick the right model for the task rather than being locked into one is exactly what I want. Some providers try to keep you inside their ecosystem. OpenCode lets you choose.

Actively maintained

The tool is actively developed. New models are added regularly, the CLI keeps improving, and the community around it is engaged.

Give it a try

If you live in the terminal and want an AI coding assistant that respects that, OpenCode is worth a try. It has become an indispensable part of my daily workflow.

If you want to give it a shot, you can sign up at opencode.ai (full disclosure: that is my referral link, which will give BOTH OF US $5 of extra credits).

Andrea

Nothing Monitors Itself: Watch Your Services Before They Silently Break

I remember the exact moment I decided I needed proper monitoring. A service I had been running for years had stopped responding. Not sure exactly when — could have been days, could have been weeks. I only noticed because someone asked me why it was down.

More importantly, it is avoidable.

Everything works until it does not

Here is the thing about self-hosted services. On install day, everything is perfect. You set it up, it works, you move on to the next thing. Days turn into weeks, weeks into months. Updates get applied, servers get migrated, DNS records get changed. And somewhere along the way, something stops working.

It is not anyone’s fault. It is just entropy. Software changes, configurations drift, certificates expire, disks fill up. The silent failures are the worst ones — the service that still responds on port 80 but returns a 500 error, the cron job that stopped running three weeks ago, the database that is about to run out of disk space.

If nobody is watching, you will not know until somebody complains.

A different angle on the same service

One thing I learned over time is that a single monitor is rarely enough. If you run a web server, you might think checking the homepage is sufficient. But what happens when the HTTP-to-HTTPS redirect breaks? Your site loads on a direct HTTPS hit but not when someone types example.com into their browser.

I add multiple monitors for the same service but looking at different angles. A monitor for the canonical URL, one for the redirect chain, one for the specific port. When I migrate a service from one server to another, the old port monitor immediately goes red — that is my reminder to update the firewall or update the DNS before anyone notices.

Misconfigurations happen over time. A network change here, an upgrade there, a firewall rule that gets lost during a server migration. Having monitors at different levels catches these before you forget.

Monitoring is not just for enterprises

There is a common belief that monitoring tools are for big teams with dedicated ops people. That you need a full-time person to manage Prometheus and Grafana before you can know whether your services are up.

I do not think that is true. For a home lab, a small business setup, or a personal infrastructure running a handful of services, you need something much simpler. You need a tool that tells you, in plain language, when something is wrong. And you need it to be easy enough that you actually set it up and maintain it.

A tool you configure once and forget about is infinitely more valuable than a powerful tool you never get around to deploying properly.

What I was looking for

When I started looking for a monitoring solution, I had a few requirements. It needed to be self-hosted — no third-party services storing information about my infrastructure. It needed to be lightweight — I did not want to spin up a separate server just to watch my other servers. And it needed to be simple — I wanted to add a monitor, configure a notification, and move on.

I also wanted something I could automate. Clicking around a dashboard to add monitors gets old when you have dozens of services. I wanted my monitoring configuration to live in the same place as the rest of my infrastructure — version-controlled, repeatable, recoverable.

What I ended up with

I have been using Uptime Kuma for a few years now and I am very happy with it. It is open source, self-hosted, APIs for configuratioand runs in a single Docker container. It checks HTTP endpoints, TCP ports, DNS records, SSL certificates, and more. It sends alerts to Telegram, Discord, email — whatever you prefer. The web UI is clean and intuitive.

This sort of basic monitoring is the entry point to running reliable infrastructure. Grafana, Loki, Prometheus and the rest are powerful tools, but they are also harder to configure and you can easily get lost in the details. A simple monitoring-only tool that does one thing well is a far more useful starting point.

Even if you only run three services — Nextcloud, Home Assistant, and a Samba share — you want something like Uptime Kuma to catch sudden misconfigurations, service crashes, or the port that stopped responding after an update you forgot about.

Once you start monitoring, you end up finding more things to add to it. Your router, your printer, your IPTV device, an energy monitor on your network. It becomes a habit, and a useful one.

Andrea

AWS dynamic IP ranges

Need to know the dynamic IP ranges used by AWS in a specific region? AWS publishes this as a JSON file.

curl -s https://ip-ranges.amazonaws.com/ip-ranges.json | jq '.prefixes | .[] | select((.region == "eu-west-1") and (.service == "EC2")) | .ip_prefix' | sed 's/"//g'

This filters for EC2 in eu-west-1 (Ireland) and outputs CIDR blocks.

To check another service:

curl -s https://ip-ranges.amazonaws.com/ip-ranges.json | jq '.prefixes | .[] | select(.service == "CLOUDFRONT") | .ip_prefix' | sed 's/"//g'

Useful for firewall rules.

Enjoy!

How to soft reset Linux disks

Sometimes disks stop responding and cause system slowness because the kernel is waiting for a response that never comes. The disk appears 100% utilised in iostat even though nothing is being read or written.

To avoid rebooting, reset the device. First find it:

lsscsi

Then remove and rescan:

echo 1 > /sys/block/sdX/device/delete
echo "- - -" > /sys/class/scsi_host/hostX/scan

Replace sdX with the affected device and hostX with the SCSI host number from lsscsi.

Hope it helps!

Find sdx device/ATAx mappings

When dealing with hardware, you often see log messages like:

ata9: hard resetting link
ata9: SATA link up 1.5 Gbps (SStatus 113 SControl 310)

To map an ata number to the actual sdX device:

ls -l /sys/class/ata_device/ata9/device/host*/target*/*/block

This shows which sdX corresponds to ata9. Check dmesg for the initial discovery mapping too.

Enjoy!

Convert Squid/timestamps to a human readable format

Somebody had the brilliant idea of using Unix timestamps in Squid log files. It makes parsing easy but reading them is not so human-friendly.

This is an elegant way of solving the problem:

cat /var/log/squid/access.log | perl -p -e 's/^([0-9]*)/"[" . localtime($1) . "]"/e'

The Perl one-liner captures the timestamp at the beginning of each line, converts it to a human-readable date with localtime(), and replaces it inline. The /e flag tells Perl to evaluate the replacement as code rather than a string.

Hope it helps!

sed – match and replace BIND/DNS serial number programmatically

I had to make modifications to several BIND zone files. Sed proved itself invaluable once again.

The zone files looked like this:

domain.com. IN SOA ns1.domain.com. postmaster.domain.com. (
               2015021001   ; Serial
               600          ; Refresh (10 minutes)
               1800         ; Retry (30 minutes)
               3600000      ; Expire (1000 hours)
               3600         ; Minimum (1 hour)
               )

To update the serial number on every zone file at once:

sed -i 's/[0-9]\{10\}\s*; Serial/2016020101   ; Serial/' *.zone

This matches a 10-digit number followed by optional whitespace and “; Serial”, then replaces it with the new serial. The -i flag edits files in place — test without it first to see what would change.

Enjoy!

Remove all non-ascii characters with perl

Sometimes when creating one-liners, the source contains non-ASCII characters that make parsing harder. To strip them all out, use Perl:

cat dirty-source.txt | perl -pe 's/[^[:ascii:]]//g' > clean-output.txt

This replaces every character that is not in the ASCII set with nothing, leaving only clean ASCII text. The same approach works for Windows line endings or other invisible characters by adjusting the character class.

If you prefer a sed alternative for the same task:

sed 's/[^[:print:]]//g' dirty-source.txt > clean-output.txt

Hope it helps!

Converting spreadsheets to text files with ssconvert

ssconvert is a utility shipped with Gnumeric, a command-line spreadsheet converter. It is useful when you have data in a spreadsheet and want to parse it from a script.

ssconvert -O 'separator=;' spreadsheet.ods spreadsheet.csv

This converts an ODS file to CSV using a semicolon as separator. ssconvert supports ODS, XLS, XLSX, CSV, HTML, and more.

You can also export specific sheets:

ssconvert -O 'separator=|;format=raw' --export-type=Gnumeric_stf:stf_csv spreadsheet.ods sheet2.csv

The format=raw option prevents ssconvert from reformatting numbers.

Enjoy!