We Built Our Company Website in Two Days with Elixir, Phoenix, and Claude Code

How iMORPHr went from an empty directory to a fully deployed, SEO-optimised company website in 48 hours — using Elixir/Phoenix and AI-assisted development with Claude Code.

iMORPHr · · 8 min read

On Wednesday morning, iMORPHr had no website. By Thursday evening, we had a fully deployed, SEO-optimised marketing site with three service pages, a blog with five articles, an RSS feed, an XML sitemap, a contact form, dark mode, CI/CD, and automatic SSL — all running on a Hetzner VPS for under £5 a month.

This is the honest story of how we did it, why we chose Elixir and Phoenix over the obvious alternatives, and how Claude Code turned what would normally be a two-week project into a two-day sprint.

Why Not Just Use WordPress?

It’s the question everyone asks. WordPress powers 40% of the web. You can have a site up in an afternoon with a theme and some plugins.

But we build software for a living. We know what happens to WordPress sites over time: plugin conflicts, security patches, database bloat, performance degradation, and the creeping anxiety of knowing that your business’s front door runs on a stack you don’t fully control.

We also considered static site generators like Hugo or Astro, and JavaScript frameworks like Next.js. Hugo is fast but limited — the moment you need a contact form or dynamic behaviour, you’re bolting on third-party services. Next.js is powerful but brings a heavy JavaScript runtime, complex deployment requirements, and React’s ongoing churn.

We wanted something different: server-rendered HTML that loads instantly, a stack we could extend with real backend logic when needed, and a framework that would still make sense in five years. That pointed us to Phoenix.

Why Elixir and Phoenix

Phoenix is a web framework built on Elixir, which runs on the BEAM — the same virtual machine that powers telecom systems designed for 99.999% uptime. It’s not trendy. It’s not what bootcamp graduates reach for. But it has properties that matter deeply for a professional website:

Sub-millisecond response times. Phoenix serves fully rendered HTML from the server. There’s no JavaScript bundle to download, parse, and hydrate. The browser gets complete HTML and renders it immediately. Our pages consistently score under 200ms Time to First Byte.

SEO by default. Search engine crawlers see exactly what users see — complete HTML with all the meta tags, structured data, and content already in the response. No client-side rendering issues, no hydration mismatches, no “please wait while JavaScript loads” problems.

No database overhead for content. We use NimblePublisher, a library that compiles Markdown files into the application at build time. Blog posts live in the git repository as plain text files. At runtime, they’re already in memory — zero database queries, zero latency. The blog is literally as fast as the framework can serve a response.

Long-term stability. Elixir and Phoenix have a reputation for backwards compatibility. Projects written years ago still compile and run on modern versions with minimal changes. The BEAM VM is 30+ years old and battle-tested. We won’t be forced into a painful migration because a JavaScript framework decided to reinvent its API.

Day One: Planning to Feature-Complete

10:00 AM — The Plan

We started with Claude Code, Anthropic’s AI-powered CLI tool. Rather than diving straight into code, we spent the first 30 minutes planning. Claude Code helped us define the architecture: no database (content compiled at build time), dead views instead of LiveView (better TTFB for static content), NimblePublisher for the blog, and Swoosh for contact form emails.

The plan covered seven phases: project bootstrap, SEO infrastructure, core pages, SEO utilities, blog system, performance optimisation, and security hardening. Every phase had specific deliverables and a verification checklist.

10:30 AM — 82 Files in One Commit

This is where AI-assisted development shows its real value. With the architecture decided, Claude Code generated the entire initial codebase: the Phoenix project structure, all controllers and templates, the SEO module with Open Graph and Twitter Card meta tags, JSON-LD structured data (Organization, Article, BreadcrumbList schemas), the NimblePublisher blog system, XML sitemap, RSS feed, contact form with Swoosh email delivery, custom plugs for cache control and trailing slash redirects, and a comprehensive test suite.

82 files. 5,524 lines of code. Reviewed, tested, and committed by lunchtime.

That doesn’t mean Claude wrote everything and we blindly accepted it. Every file was reviewed. Architecture decisions were challenged. Templates were adjusted to match our brand voice. But the fundamental acceleration is real — instead of spending hours on boilerplate (router configuration, meta tag templates, XML feed formatting, test setup), we spent that time on decisions that actually matter.

1:30 PM — Security Hardening

Phase 7 was security. Claude Code’s review caught several issues that needed fixing:

  • Email header injection in the contact form — the original implementation put user-supplied data directly in the from: field, which could allow CRLF injection
  • Missing input validation — no length limits, no email format validation, no graceful error handling
  • JSON-LD script injection risk — the structured data output needed HTML-safe encoding
  • Missing security headers — Content Security Policy, secure session cookies, and secure headers on the sitemap scope

We also added a honeypot field for spam detection and wrote comprehensive tests: 52 test cases covering the contact form, blog rendering, sitemap generation, and SEO metadata.

3:00 PM — Service Pages

With the core site solid, we built out the three service pages: ERPNext Implementation, Custom Software Development, and DevOps & Cloud Services. Each page has its own controller action, detailed template with service breakdowns and industry-specific content, full SEO metadata, and a companion blog post.

By 5:42 PM on day one, we had a feature-complete website with five blog posts, three detailed service pages, and all the SEO infrastructure a modern site needs.

Day Two: Deployment and Polish

8:00 AM — Docker and CI/CD

Day two started with deployment. We generated a Dockerfile using mix phx.gen.release --docker and set up a GitHub Actions workflow that builds a Docker image, pushes it to GitHub Container Registry, SSHs into our Hetzner VPS, and restarts the container.

The first build failed — the slim Debian base image couldn’t download Tailwind CSS because it lacked SSL certificates. A one-line Dockerfile fix (apt-get install ca-certificates) solved it. We also hit the Docker Compose V1/V2 syntax difference (docker-compose vs docker compose) on the server, which required installing the compose plugin.

These are the kinds of issues that eat hours when you’re deploying for the first time. Having Claude Code to diagnose and fix them in minutes rather than hours made a real difference. We wrote up the full deployment setup in a separate post: Zero-to-Production: Deploying Phoenix to Hetzner with Docker, GitHub Actions, and Caddy.

1:30 PM — Navigation and Branding

With the site live, we shifted to visual polish. We replaced the basic footer navigation with a proper responsive navbar using DaisyUI’s drawer component — full horizontal menu on desktop, hamburger drawer on mobile. We added logo variants for light and dark mode, created an SVG favicon, and updated the colour scheme.

4:00 PM — Content Refinement

The afternoon was spent on content: updating the company positioning from “digital agency” to “technology agency” across every template, controller, and SEO module (our CLAUDE.md file now has a rule about grepping the entire codebase when changing brand copy — we learned that lesson during this session). We removed blog posts about services we don’t offer, improved mobile typography scaling, and wrote the deployment blog post documenting the Hetzner + Docker + Caddy + GitHub Actions setup.

5:47 PM — Done

27 commits. Two days. A production website serving real traffic with automatic SSL, CI/CD deployments, and comprehensive SEO.

What Claude Code Actually Did (and Didn’t Do)

Let’s be specific about the AI’s role, because the hype around AI-assisted development often obscures the reality.

What Claude Code did well:

  • Generated consistent, idiomatic Phoenix code that followed framework conventions
  • Built the entire SEO infrastructure (meta tags, JSON-LD, sitemaps, RSS feeds) correctly on the first pass
  • Caught security issues during code review that would have shipped otherwise
  • Diagnosed deployment problems (SSL certificates, Docker Compose versioning) quickly
  • Maintained context across the entire codebase — when we changed the company description, it knew every file that needed updating

What it didn’t do:

  • Make architecture decisions for us. We chose dead views over LiveView, NimblePublisher over a database, Hetzner over Fly.io. Those decisions came from experience and business requirements
  • Write our brand copy. The service descriptions, about page narrative, and blog content voice are human
  • Replace code review. Every generated file was read, understood, and sometimes rewritten
  • Handle visual design. Colour choices, typography, layout decisions, and logo work were all manual

The honest assessment: Claude Code compressed roughly two weeks of solo developer work into two days. Not by writing perfect code — by eliminating the mechanical overhead that sits between “I know what I want to build” and “it’s built.” The time savings came from boilerplate generation, configuration scaffolding, test writing, and rapid debugging — not from replacing engineering judgment.

The Final Stack

For anyone considering a similar approach, here’s exactly what we’re running:

  • Framework: Phoenix 1.8.4 on Elixir 1.19.3
  • Content: NimblePublisher (Markdown blog posts compiled at build time)
  • Styling: Tailwind CSS + DaisyUI
  • Email: Swoosh (contact form submissions)
  • Hosting: Hetzner VPS (under £5/month)
  • Reverse Proxy: Caddy (automatic Let’s Encrypt SSL)
  • CI/CD: GitHub Actions → Docker → GitHub Container Registry → SSH deploy
  • AI Tooling: Claude Code (Anthropic’s CLI)

No database. No JavaScript framework. No CMS. No monthly SaaS subscriptions for hosting, forms, or analytics. Total running cost: the price of a coffee.

Would We Do It Again?

Without hesitation. Phoenix gave us a fast, maintainable foundation that we can extend when needed — adding LiveView for interactive features, connecting a database for client portals, or scaling to handle traffic spikes. Claude Code gave us the velocity to go from nothing to production in a timeframe that would be unreasonable with traditional development.

The combination of a well-designed framework and intelligent AI tooling isn’t a gimmick. It’s a genuine multiplier for experienced developers who know what they want to build. The key word is “experienced” — you still need to know what good code looks like to review what the AI produces. But if you have that expertise, the speed is real.

Two days. Twenty-seven commits. One website. Live.