Ikena
2024 - 2025
A map-based web app for exploring and sharing geographic points of interest. I engineered the backend API with Bun and Hono, featuring an async image pipeline with BullMQ, Sharp, and Cloudflare R2 storage.
About
Ikena started as a simple question: what if discovering interesting places felt as natural as scrolling a feed? You open the map, see what is around you, and drop a pin on anything worth remembering.
It is the kind of app I wanted to exist, so I built it.
Architecture
The backend is a Bun-powered Hono API handling authentication, image processing, and spatial data queries. The frontend is a Remix SSR application built around an interactive MapLibre GL map.
Image uploads flow through a background pipeline. The API accepts the raw upload and queues it with BullMQ. A worker generates an optimized full-size image and a thumbnail with Sharp, then pushes both to Cloudflare R2.
This keeps the upload endpoint fast while clients always receive optimized images. The database is Supabase (PostgreSQL) with the PostGIS extension for spatial queries, and Redis backs the job queue.
The API did not start on this stack. The first version ran on Deno with Oak and Kysely. I later rewrote it on Bun and Hono, with OpenAPI documentation generated from the same Zod schemas that validate every route.
The whole stack runs on a Hetzner VPS managed through Coolify: API, worker, Redis, and frontend as separate containers, with uptime monitoring and log aggregation keeping production observable.
My Role
I built the entire backend from architecture to deployment, plus the map interaction layer, data fetching, and components on the frontend. I also owned the infrastructure: the Hetzner VPS, Coolify, domains, SSL, CI/CD, and monitoring.
What I Learned
This project pushed me to design APIs that acknowledge work without blocking on it. BullMQ was my first production job queue, and getting the retry and dead-letter logic right for the image pipeline taught me a lot about resilience.
Geospatial data changed how I think about database indexing. A naive ST_DWithin query on unindexed geometry columns took 3 seconds. After adding GiST indexes and tuning the query planner, it dropped to under 50ms.
On the DevOps side, I learned to own a deployment end to end. Provisioning a raw VPS, wiring up Coolify, reverse proxies, SSL, and CI/CD showed me what actually happens between git push and a live production URL.
Today
Ikena is live at ikena.app. The backend has already survived one full rewrite, and the map keeps collecting places.