Seeding a Room Database via ADB BroadcastReceiver in Kotlin

Real codebase

The code in this article is extracted directly from a production Kotlin Multiplatform personal finance app. All snippets reflect actual implementation, not simplified examples.

Manually tapping through a finance app to recreate accounts, transactions, budgets, and subscriptions every time you wipe the database is not a workflow. It is a tax on iteration speed. This post covers a BroadcastReceiver based database seeder for a Kotlin Multiplatform personal finance app that lets you populate Room entities with a single terminal command. Three non-obvious bugs make this harder than it looks all of them are covered here.

AtomBrick: ESP32 BLE Motor Controller to KMP Mobile App

AtomBrick is a Bluetooth motor controller. An ESP32 acts as a BLE peripheral driving up to five DC motors. A Kotlin Multiplatform app on Android and iOS connects, authenticates via passkey, and sends motor commands. This post walks the full stack, covering firmware, command protocol, and the mobile layer, with specific attention to where the two sides meet.

BLE GATT Profile Design for Hardware Control: AtomBrick

When you define a BLE GATT profile for the first time, it feels like boilerplate: pick some UUIDs, create a service, add characteristics. Those decisions define the protocol between your firmware and every app that ever connects to the device. AtomBrick went through one revision of this, and the mistakes are specific enough to be worth documenting.

ESP32 BLE Passkey Pairing, Bonding, and Encryption

The default ESP32 BLE example from Espressif ships with zero authentication. Any device in range can connect, read, and write your characteristics. For a product controlling physical actuators, that is not acceptable. This post shows how to add passkey pairing, MITM protection, and bonding to an ESP32 BLE server using the Arduino BLE stack, drawn from a real motor controller firmware.

Kotlin Multiplatform BLE with Kable, Koin, and Compose

Android BLE is BluetoothGatt callbacks firing on arbitrary threads. iOS BLE is CBCentralManager delegates with its own threading model. Kable, a KMP BLE library by JUUL Labs, wraps both behind a coroutines and Flow API. This post shows how the AtomBrick motor controller app uses Kable alongside Koin and Compose Multiplatform to scan, connect, and send commands to an ESP32 peripheral from a single shared codebase.

Android Project Docs with MkDocs, Dokka, and Firebase Hosting

Most Android projects ship without documentation. The few that have it rely on a Confluence page that is out of date by the time it is merged. This post covers the setup used in Unizonn Mobile v2: MkDocs Material for the documentation site, Dokka for generated API reference, Firebase Hosting for deployment, and a single GitHub Actions workflow that builds and ships everything on every PR targeting main or staging. The result is a live URL that reviewers can open from a pull request without cloning anything.

Traefik v3: Routing Multiple Docker Apps on One Server

Running five Docker apps on one server creates an immediate problem: which port goes to which service, and how do you keep it from becoming a mess as the stack grows? The usual answer is Nginx with manually maintained config blocks. That works until a container restarts with a new IP and your proxy is pointing at nothing.

Traefik solves this by watching the Docker socket directly. When a container starts with the right labels, Traefik builds the route. When it stops, the route disappears. No config reload, no manual upstream blocks, no downtime.

This post covers a production-ready Traefik v3 setup with named entrypoints per service, a locked-down dashboard, and Docker-label-based routing. Every other post in this series builds on this foundation.

Assumed knowledge: Docker Compose basics, what a reverse proxy does.

Traefik v3: Automatic Routing, TLS, and Middleware for Docker

Running multiple Docker apps behind Nginx means writing upstream blocks, running nginx -s reload, and doing it again every time a container restarts with a new IP. Nginx is a file-driven proxy built for static infrastructure. Docker is the opposite: containers die, restart, scale, and get replaced continuously. The mismatch is not subtle, it creates a permanent maintenance loop.

Traefik eliminates that loop. It watches the Docker socket directly. When a container starts with the right labels, Traefik builds the route. When the container stops, the route disappears. No config files per service, no reload commands, no manual certificate management.

This post covers Traefik's architecture, a direct comparison with Nginx and Nginx Proxy Manager, and a complete production setup: static config, Docker Compose, label-based routing, Let's Encrypt TLS, HTTP-to-HTTPS redirect, path-based routing, and dashboard access via SSH tunnel.

Assumed knowledge: Docker Compose basics, what a reverse proxy does.

Self-Hosted Dev Infrastructure with Docker Compose

Most dev setups run a separate Postgres, Redis, and MinIO for every project. Six containers doing the same job, different passwords, none of them shared. This post replaces that with one docker-compose.yml: a shared infra stack on a named Docker network that any app can join without owning or duplicating the services. By the end you'll have Traefik routing traffic, PostgreSQL and Redis running internally, MinIO handling object storage, and Mailpit catching all outbound email. Assumed knowledge: Docker basics and .env files.

Docker on a VPS: UFW, Port Bindings, and SSH Tunnels

Putting a Docker Compose stack on a public VPS without thinking about network exposure is one of the most reliable ways to get your infrastructure compromised. Docker bypasses UFW by default. A 0.0.0.0 port binding on Postgres means your database is reachable from anywhere on the internet, regardless of what your firewall rules say.

This post covers the exact approach used across this series: which ports go on 0.0.0.0, which stay on 127.0.0.1, how UFW and Docker interact at the iptables level, and how to reach locked-down services from your local machine using SSH tunnels. No third-party tools, no complex firewall rules. Just two knobs that actually work.

Assumed knowledge: Linux basics, Docker Compose, what UFW is.