This stack reflects my own constraints and preferences, not a universal prescription. If your context, scale, or existing toolset differs, your choices should probably differ too. Treat this as input to your own design process β not a blueprint to copy. You are responsible for evaluating whether any of this is appropriate for your situation.
Why This Stack?
The term Rapid Application Development (RAD) carries baggage β in the nineties it described visual builders and code generation from forms. I am using it literally: building applications rapidly, without ceremony. The target is a specific class of problem β prototypes, internal tools, exploratory builds, and focused production services where operational overhead is the enemy of progress. For all of these, the answer is one binary, one database file, one host to manage.
That answer kept repeating across projects, but handled ad-hoc each time, the result was inconsistency β good decisions remade from scratch, shortcuts that looked temporary but became permanent, a foundation rebuilt rather than reused. This stack is the decision to stop doing that: make the starting-point choices once, deliberately, and build a template the next project can start from without ceremony.
Some of these components have been used on real projects β those I have direct confidence in. Others I have used individually but not yet in this exact combination. A few are here because the design rationale is sound, but only use will confirm they hold up as a whole. This is a formalized hypothesis, not a proven recipe. The template exists. The validation is ongoing.
The Philosophy
These goals are the lens through which every technology choice is evaluated. When trade-offs arise, they are resolved by asking which option best serves these outcomes.
| Goal | What it means |
|---|---|
| Function over form | The application works correctly and reliably. Aesthetic refinement is deferred β a working tool with a plain interface beats a polished prototype that does not quite function. |
| Fast and pleasant to develop | A fast rebuild loop, clear structure, and no tooling friction. The codebase has a low framework-specific mental model β the patterns are standard and explicit, with minimal magic to decode. |
| Simple to deploy, deployable everywhere | Deploying the application is a single, repeatable step β no environment-specific build process, no container registry, no orchestration layer. The same artifact runs anywhere. |
| Simple to operate | Fewer moving parts means fewer things to break, patch, or upgrade. Low runtime overhead, a small monitoring surface, and standard tooling for observability β no specialist knowledge required. |
| Easy to evolve | When requirements change β and they will β the stack should not resist. Individual parts can be swapped for more mature alternatives when the need arises, or broken out into separate services when the application outgrows the monolith. No framework lock-in, no tight coupling between layers that makes either move expensive. |
The Principles
The principles translate the philosophy into concrete engineering decisions. They are technology-agnostic β the same principle can be satisfied by different implementations. The Technology Choices section maps each principle to specific tools.
Application
| Principle | Rationale |
|---|---|
| Flexible output modes | The application can run as a standalone background service with no user interface, a web API, a browser-rendered web application, or any combination. The same stack supports all three without switching technologies or introducing separate runtimes. |
| The browser is the GUI platform | All user-facing interfaces are delivered through the browser β no native clients, no thick clients, no separate runtime to install. Internal tools use the browser for the same reason external products do: it is universal, requires no deployment to end users, and works everywhere. |
Frontend Architecture
| Principle | Rationale |
|---|---|
| Server-first architecture (SSR) | Logic, data, and rendering live on the server. The server sends complete HTML β the browser renders it, not assembles it. No client-side state management, no API contract between frontend and backend, no hydration step. |
| Progressive enhancement | Dynamic interactions update specific parts of the page, not the whole page. The server responds with targeted HTML fragments that replace sections of the DOM β no full-page reloads, no client-side construction from JSON. |
| Minimal client-side JavaScript | Client-side JavaScript is added when genuine client-side interactivity is needed and sprinkled on top of working HTML β not used as the foundation. The baseline works without it; JavaScript enhances it. |
| Semantic, class-light styling | HTML elements are styled directly through a semantic CSS baseline β no utility-class proliferation, no CSS-in-JS, no build-time stylesheet generation. Markup stays clean and readable; CSS follows the content structure rather than decorating it with class names. |
Deployment & Infrastructure
| Principle | Rationale |
|---|---|
| Common denominator infrastructure | The target deployment environment is a VM or bare metal with an OS. No container runtime, no managed services, no cloud-provider-specific primitives β anything that runs Linux can run this application. |
| Lean on the platform | Where the OS or platform already provides a capability β process management, scheduling, logging β the stack uses it rather than replacing it. Every additional service must earn its place; nothing is added speculatively. |
| Protected ingress | The application is never exposed directly to the internet. All public traffic enters through a protection layer β a Cloudflare Tunnel or equivalent. No inbound ports on the host, no public IP required, no direct attack surface. |
| Monorepo | Application code, configuration, scripts, and documentation all live in a single repository β one place to find everything, one change to ship everything. No cross-repo coordination overhead. |
| Infrastructure and Configuration as Code | Infrastructure provisioning and service configuration are defined as code β version-controlled, reviewable, and repeatable. No manual steps, no undocumented configuration drift. The repository is the source of truth for both what runs and how it is configured. |
| One deployable application | Application code, templates, and static assets are bundled into a single deployable unit β no separate asset pipeline or content server required. |
| Embedded persistence | Application data lives in an embedded database on the same host. No separate database process, no network round-trip, no connection pool β persistence is part of the application, not a separate service to manage. |
| Production-ready from day one | The development environment mirrors production. There are no βit works locallyβ surprises because local and production run the same application. |
| Deploy from local machine | The application can be deployed directly from a developer machine. A CI/CD pipeline can be added when it earns its place β it is not a prerequisite for shipping. |
| Multi-tenancy by deployment | Multi-tenant isolation is achieved by deploying separate instances, not by building tenant partitioning into the application. Each tenant gets a dedicated binary, database, and host β complete isolation with no shared-state coordination. The stackβs low operational cost makes per-tenant deployment practical. |
| Scale up before out | A single well-resourced host handles more load than most applications built with this stack will ever see. Vertical scaling β more CPU, RAM, and faster storage β is the first response to capacity pressure. If a single well-resourced host is no longer enough, the application has probably outgrown the scope of this stack β and the right response is rearchitecture, not forcing horizontal scaling onto a foundation that was never designed for it. |
| Recovery over redundancy | The target is recovery time, not zero-downtime failover. The stack is designed to be fully recoverable from any failure mode without manual guesswork β through VM snapshots, continuous data replication, and automated infrastructure provisioning. |
Quality
| Principle | Rationale |
|---|---|
| Fail at build time, not runtime | Where possible, errors are caught at compile time rather than discovered in production. Type safety across the stack β templates, data access, API boundaries β ensures whole classes of bugs surface during development, not after deployment. |
| Generate from source of truth | Where a higher-level artifact defines the contract β a template, a schema, an API specification β implementation code is generated from it rather than written by hand. The generated code is always in sync with its source, and inconsistencies surface at build time rather than at runtime. |
| Test the interfaces, not the internals | Automated tests cover the critical paths β the API surface and GUI behavior that must not break. Tests target the observable boundaries of the application (APIs, GUI interactions, file outputs), not internal implementation details. This keeps the test suite stable as the codebase evolves, and avoids testing for coverage metrics rather than confidence. |
| Manual exploration | Manual exploration catches what tests do not anticipate. Running and using the application directly surfaces edge cases, usability issues, and unexpected interactions. |
The Technology Choices
Every component earns its place. The tables below map the full stack across twelve groups β from the application code down to the physical infrastructure β and explain what each component contributes and why it is there. Not every group applies to every application, and not every function within a group does either β this is a reference set to draw from, not a mandatory checklist. A background service needs none of the frontend rows; an API-only application skips the templating layer; a simple internal tool may never need session management or analytical storage. Each application takes what it needs.
Application (server)
| Function | Technology | Purpose |
|---|---|---|
| Programming Language | Go programming language | Statically compiled with no external runtime β the binary is the complete application. Go specifically: fast compile times keep the rebuild loop tight; goroutines and channels handle concurrency and long-running background services natively; and the standard library covers HTTP, JSON, cryptography, and structured logging without external packages. Deployment is copying a file β no runtime to install, no version to manage. |
| Router | Chi | Routing and middleware. Lightweight, idiomatic, and explicit β no magic, no framework conventions to decode. |
| Input Validation | go-playground/validator | Struct-level validation for incoming request data. Validates fields by tag β no custom validation boilerplate for common constraints. |
| Password Hashing | golang.org/x/crypto | Bcrypt hashing for credential storage and verification. Passwords are stored as bcrypt hashes and verified at login against the submitted value β no additional runtime, no external service. |
| Session Management | alexedwards/scs | Server-side session management backed by SQLite β no separate session store required. Stores the authenticated user identity and role in the session after login. Chi middleware reads the session to enforce role-based access per route group. |
| Static Assets | Go embed | HTML, CSS, JS, images, and fonts bundled into the application binary β no separate asset pipeline or content server required. |
| Data Access | sqlc | Type-safe SQL queries generated from annotated .sql files. SQL is validated at generation time β query errors surface before deployment, not at runtime. |
| Database Driver | modernc.org/sqlite | Pure Go SQLite driver β no CGO required. Keeps the binary self-contained and the build process simple. |
| Schema Migrations | goose | Database schema migration tool. Migrations are plain SQL or Go functions, version-controlled alongside the application code. Applied at startup or via a Task target. |
| Structured Logging | slog | Structured, levelled log output written to stdout. Captured and retained by the OS journal β no separate log aggregation required. A request ID is attached to every log entry via middleware, making cross-entry correlation straightforward with standard tools like journalctl and jq. |
| Health Check | /healthz | Lightweight endpoint that returns the application status. No dependencies, no auth β designed to be called from a Task target for quick operational checks from the developer machine. |
Frontend Architecture (browser-delivered)
| Function | Technology | Purpose |
|---|---|---|
| HTML Templating | templ | Type-safe server-side HTML templates compiled at build time. Template errors surface before deployment, not at runtime. |
| Progressive Enhancement | htmx | Server-driven partial page updates. The server returns HTML fragments β no JSON, no client-side state management, no hydration step. |
| Client-side Interactivity | Alpine.js | Lightweight interactivity layer for behaviors that belong in the browser β dropdowns, modals, tab panels, conditional visibility, and reactive form interactions. Handles local state and event-driven behavior without a server round-trip and without pulling in a full JavaScript framework. |
| Styling Framework | Pico CSS | Semantic CSS baseline that styles HTML elements directly β no utility-class proliferation, no CSS-in-JS, no build step. Clean markup with sensible defaults that cover the majority of styling needs without custom overrides. |
Storage (server)
| Function | Technology | Purpose |
|---|---|---|
| Transactional Database | SQLite (WAL Mode) | Embedded relational database for operational data. No separate process, no network round-trip, no connection pool β the database is a file on disk. |
| Database Backup | Litestream | Continuous SQLite replication to any S3-compatible store or SFTP target with no impact on reads or writes. Provides point-in-time restore with near-zero operational cost. |
| Analytical Database | DuckDB | Analytical database for OLAP workloads. Accessed via the DuckDB CLI or Python analytics scripts β not embedded in the Go application binary, which avoids the CGO dependency the Go DuckDB bindings require. Queries large datasets without a separate server or ETL pipeline. |
| Analytical Files | Parquet | Columnar file format for analytical data at rest. Written by the Go application or exported from DuckDB β efficient, compressed, and readable by any analytics tooling without a database layer. |
| Plain Files | OS filesystem | For data that does not need a relational model β configuration, logs, exported reports, or structured content that fits naturally in files. Read and written directly without a database layer. |
| File Backup | restic | Encrypted, deduplicated backup of files and directories to any S3-compatible store or SFTP target. Supports incremental backups with point-in-time restore β no backup server required. |
| Object Storage | Any S3-compatible store or SFTP | Off-host destination for Litestream database replicas and restic file backups. Both tools support S3-compatible stores (Cloudflare R2, Backblaze B2, AWS S3, or self-hosted MinIO) and SFTP. SFTP makes a NAS device with SSH access a valid backup target without any additional software β no S3 compatibility required. |
Application Scripts (developer machine and server)
| Function | Technology | Purpose |
|---|---|---|
| Analytics Processing | Polars | Python library for high-performance DataFrame operations on Parquet files. Used in analytics scripts for batch processing, exports, and reporting β not embedded in the Go application. |
Build (developer machine)
| Function | Technology | Purpose |
|---|---|---|
| Compilation | go build | Compiles the application into a single statically linked binary. No linker scripts, no build system beyond the Go toolchain β one command produces the deployable artifact. |
| Template Generation | templ generate | Generates Go code from templ templates. Runs as part of the build and during development. |
| Query Generation | sqlc generate | Generates type-safe Go functions and structs from annotated .sql files. Runs as part of the build and during development. |
| API Specification | OpenAPI Spec | When the application exposes an API, the contract is defined first as an OpenAPI specification. Server stubs and types are generated from the spec β the implementation conforms to the contract, not the other way around. |
| API Code Generation | oapi-codegen | Generates Go server interfaces and types from the OpenAPI spec. Runs as part of the build alongside templ generate and sqlc generate. |
| Linting | golangci-lint | Runs go vet, gofmt, errcheck, staticcheck, gosec, and more through a single invocation with consistent output and a single configuration file. No separate tool invocations, no differing output formats β one command covers correctness, style, error handling, and security. |
| Vulnerability Scan | govulncheck | Official Go vulnerability checker. Scans dependencies against the Go vulnerability database β surfaces known CVEs before deployment, not after. |
| Secret Scanning | Gitleaks | Scans the git repository for accidentally committed secrets β API keys, tokens, passwords, and private keys. Runs as a pre-commit hook and as a Task target. Catches credential leaks before they reach the remote. |
Testing (developer machine)
| Function | Technology | Purpose |
|---|---|---|
| Unit Testing | go test | Standard Go test runner. Tests are written as plain Go functions in _test.go files alongside the code β no separate test framework required. Run via Task as part of the standard build pipeline. |
| API Testing | Bruno | API collections stored as plain .bru files alongside the code β version-controlled and diffable. Supports both manual exploration and automated runs from Task, bridging interactive testing and scripted verification. |
| GUI Testing | Playwright | Automated browser tests for GUI interactions. Tests routes, page content, and interactive behavior as a user would. |
DevOps Platform (developer machine)
| Function | Technology | Purpose |
|---|---|---|
| Version Control | Any git server | Source control. Self-hosted or managed β the stack has no dependency on a specific provider. |
| Deployment Automation | Ansible | Repeatable, idempotent deployment from a local machine. Copies the binary, manages systemd service files, and configures the host β no CI/CD pipeline required. |
| Secrets Management | Ansible Vault | Encrypts sensitive values β credentials, API keys, tokens β in a version-controlled YAML file. The vault password lives in a password manager, not the repository. Ansible decrypts at deploy time and writes secrets to a root-owned systemd EnvironmentFile on the host, injecting them as environment variables into the application process. |
| Task Runner | Task | Orchestrates common development and deployment tasks β building, testing, generating, and deploying β through a single, discoverable entry point. Tasks are defined in a Taskfile.yml alongside the code β version-controlled, readable, and self-documenting via task --list. |
| Development Reload | Air | Watches for file changes and rebuilds the binary. No bundler, no hot module replacement, no Node.js process. |
| Dev Environment | mise | Pins exact versions of every development tool β Go toolchain, templ, sqlc, golangci-lint, Air, Python, and others β in a version-controlled .mise.toml. Any developer running mise install gets the same tool versions without Docker, containers, or a Nix dependency. Also manages Python virtual environments, auto-creating and activating a .venv when entering the project directory. Coexists with system package managers β project versions shadow global installs within the project directory only. |
Platform Scripts (developer machine and server)
| Function | Technology | Purpose |
|---|---|---|
| Automation Scripts | Bash / Python | Automation scripts for tasks the Taskfile orchestrates. Bash for simple shell tasks; Python when more logic or structure is needed. |
| Health Check | task health | A Task target that curls the /healthz endpoint and reports the application status. Quick operational check runnable from the developer machine without opening a browser. |
Documentation (server β authored locally, served by the application)
| Function | Technology | Purpose |
|---|---|---|
| Prose Documentation | Markdown + goldmark | Documentation written in Markdown and rendered to HTML by the application. Served at a /docs endpoint β no separate documentation site or deployment required. |
| API Documentation | OpenAPI + Redoc | API documentation generated from the OpenAPI spec and served at /api/docs. Stays in sync with the spec that drives code generation β no separate API docs deployment. |
| Diagrams | PlantUML / Mermaid | Architecture, sequence, and flow diagrams defined in markup β version-controlled and diffable alongside the code. Generated as SVG files in the build pipeline and embedded in the /docs endpoint. |
Application Platform (server)
| Function | Technology | Purpose |
|---|---|---|
| Operating System | Rocky Linux | Hardened OS baseline with SELinux enforcing, regular security errata, and RHEL compatibility β security is part of the platform, not bolted on. |
| Mandatory Access Control | SELinux | Confines processes to the minimum access they require β a misconfigured service cannot escape its defined permissions, regardless of other controls. |
| Host Firewall | firewalld | Restricts network access to the host, permitting only what is explicitly required β SSH and any configured ingress β and blocking everything else. Reduces the attack surface at the host boundary regardless of the ingress layer in use. |
| Process Management | systemd | Manages all services on the host as independent units β the application, reverse proxy, and any supporting services. Service units define restart-on-failure behavior, resource limits, and filesystem sandboxing β application hardening configured as code. |
| Scheduled Tasks | systemd timers | Periodic and time-based task execution managed by systemd. No separate cron daemon β timer units are version-controlled, observable, and managed alongside the application services. |
| Remote Access | SSH | Secure shell access for administration and Ansible connectivity. Key-based authentication only β password authentication and root login are disabled. A dedicated deploy user with limited sudo permissions is the only account Ansible uses. |
| OS Updates | dnf-automatic | Automatic security patch application for all dnf-managed packages β OS, Caddy, and cloudflared all provide official RPM repositories and are updated automatically. Binary installs not managed by dnf (Litestream, restic) are version-pinned in Ansible and updated on each deployment run. |
| DNS | Cloudflare DNS / self-hosted | Maps the domain name to the Cloudflare Tunnel endpoint. Internal or air-gapped deployments use a self-hosted DNS resolver β any DNS server that resolves the application hostname on the local network. No dependency on a specific DNS provider. |
| Public Ingress | Cloudflare Tunnel | The sole entry point for public internet traffic. Connects the host to Cloudflareβs network without opening any inbound ports β DDoS protection, access control, and TLS termination without exposing a public IP address. |
| Reverse Proxy | Caddy | Reverse proxy between the ingress layer and the application. Handles routing, response compression, and security header injection. Provides automatic HTTPS β certificates are managed without manual configuration, whether traffic arrives from a Cloudflare Tunnel, a local network, or directly from the internet. |
Virtual Infrastructure (server)
| Function | Technology | Purpose |
|---|---|---|
| Bare Metal | Direct OS install | Rocky Linux installed directly on the hardware β no hypervisor layer. The simplest self-hosted option when VM isolation and snapshots are not required. |
| Hypervisor | Proxmox | Self-hosted virtualization on own hardware. Manages Rocky Linux VMs with isolation, snapshots, and resource allocation. The choice when deploying locally rather than through a cloud provider. |
| Cloud VM | Any cloud provider | When not self-hosting, any cloud providerβs VM service takes the place of Proxmox. The stack has no cloud provider dependency β the VM only needs to run Rocky Linux. |
| VM Snapshots | Proxmox Backup / Cloud Provider | Scheduled VM snapshots provide a full-system recovery point at the hypervisor level β OS, configuration, application binary, and database in a single restore operation. Proxmox includes built-in VM backup scheduling; cloud providers offer equivalent snapshot services as standard. For bare metal without a hypervisor there is no snapshot layer β recovery falls back to Ansible provisioning and data restore from Litestream and restic. |
Physical Infrastructure (server)
| Function | Technology | Purpose |
|---|---|---|
| Compute | Bare metal / Raspberry Pi | The physical host when not using a cloud provider. Runs Rocky Linux directly or Proxmox as the hypervisor β any hardware they both support. A repurposed server, a NUC, or a Raspberry Pi all qualify. |
Roads Not Taken
The section title is a deliberate β if not particularly clever β nod to Robert Frost. Several roads diverged. The ones below are real paths β well-worn, with good reasons to take them. This stack took a different one, and these are the forks worth explaining.
| Alternative | Why not |
|---|---|
| Containers (Docker Compose, Kubernetes) | The application is a single statically compiled binary β Docker Compose adds a runtime, image management, and volume overhead for problems this stack does not have. systemd manages the process with lower overhead and a tighter security model. Kubernetes scales these concerns up, not down. |
| Fullstack frameworks (SvelteKit, Next.js) | Designed for stateless, short-lived request handlers β not long-running background services or direct OS integration. Go handles the full range: one binary, managed as a daemon, with goroutines and signal handling a Node-based framework is not built for. templ and htmx cover the frontend without pulling a bundler or a second runtime into the deployment. |
| TypeScript everywhere (Hono, Elysia) | Go produces a single statically linked binary with no runtime and no node_modules on the server. Goβs concurrency model β goroutines and channels β handles parallel workloads in ways that Node, Bun, and Denoβs event loop model is not designed for. |
| ORM (GORM, ent) | sqlc generates type-safe Go functions directly from annotated SQL β readable, reviewable, and validated at build time. ORMs abstract this behind a query builder, adding an API surface to learn and debug around without a corresponding gain for this stackβs data access patterns. |
| PostgreSQL | SQLite in WAL mode handles this stackβs read/write patterns on a single host with no separate process, no connection pool, and no network round-trip. When the application outgrows it, the migration path to PostgreSQL is well-understood β the stack is designed not to resist it. |
| CI/CD pipeline | The stack deploys directly from a developer machine via Ansible β one command, repeatable, idempotent, and auditable. For a solo developer or small team, pipeline overhead adds coordination cost before it adds value. The stack is not designed to resist it when the time comes. |
| PaaS (Railway, Fly.io, Render) | A bare VM has a fundamentally wider operational envelope β full control over the process, filesystem, and network. PaaS requires containers, constrains what the application can do, and creates a vendor dependency that the βdeployable everywhereβ principle explicitly avoids. |
The Bottom Line
The RAD Stack is not a universal answer β it is a specific set of trade-offs made for a specific category of problem. It gives up horizontal scalability for operational simplicity. It gives up client-side flexibility for server-side predictability. It gives up ecosystem breadth for deployment clarity. For prototypes, internal tools, and focused production services β where getting to working, maintainable software quickly matters more than planning for scale that may never arrive β those are not losses. They are the design.
Most of the technologies in this stack have track records in production individually β those carry direct confidence. A few are here on the strength of design rationale rather than personal use. What remains a hypothesis in either case is this specific combination β that they hold together as coherently as they hold apart. Building with the stack is how that gets tested.
Faced the same constraints β rapid iteration, minimal overhead, production-ready without a platform team β but arrived at different principles or a different stack? The disagreements are the interesting part. Iβm interested in the conversation.
The hypothesis gets tested as I build with it. Iβll document what holds up and what needs revisiting. Follow my RSS feed to catch what comes next.