Every client project costs the same amount of time — whether it’s the first or the fiftieth. After 20 identical contact forms, 20 identical SEO setups, and 20 identical Vercel deployments, I asked myself: Am I building websites, or am I building a system that builds websites?
The answer changed my business model.
The Time-for-Money Trap in Freelance Web Development
Freelance web development has a fundamental scaling problem: 80% of requirements are identical across every project.
Contact form. Local SEO. Mobile-optimized navigation. Multi-language content. Schema.org markup. Cookie banner. Legal pages. Privacy policy. Google Analytics. Sitemap. Fast deployment.
The remaining 20% is custom: branding, specific content, maybe a few custom features.
But you’re building from scratch every time.
The math is brutal: A typical SMB website project costs 40-60 hours of development time. At $90-120/hour (average for experienced freelancers in the US/EU), that’s $3,600-7,200 per project. Sounds good — until you realize that at 5 projects per month, you’re working 250 hours. That’s 62.5 hours per week.
Not scalable. Not sustainable. Not smart.
The question isn’t whether you can work faster. The question is whether you can change the system.
The Architecture: Standalone Templates + Shell Script Generator
I decided against WordPress. Not because WordPress is bad — it’s built for content editors who want to work without developers. My goal was different: I wanted to deploy fast as a developer, without monthly hosting costs for clients, without database maintenance, without plugin updates.
Why Astro?
Astro is static-first. That means: no database, no server costs, no security vulnerabilities from outdated plugins. The output is pure HTML, CSS, and JavaScript. Lighthouse scores of 95+ are standard, not exception.
The component architecture is perfect for template building blocks. A <ContactForm>, a <ProductGrid>, a <HeroSection> — everything reusable, everything type-safe, everything versioned.
Why a shell script instead of a no-code generator?
Because the goal isn’t an app — the goal is control.
The script is 200 lines of Bash. It’s readable, versionable, extensible. No SaaS subscription, no vendor lock-in, no monthly costs. I can run it in 5 years without worrying about a startup going bankrupt.
./create-customer-project.sh
# Questions:
# - Industry? (Bakery, Restaurant, Consulting, Blog)
# - Market? (US, UK, DE, MX, CO, AR, CL, PE, ...)
# - Domain?
# - Primary color?
# Output:
# → Complete Astro project
# → Localization (14 markets: DACH + Latin America)
# → SEO configuration (Schema.org, Sitemap, Meta tags)
# → Legal texts (Imprint, Privacy, country-specific)
# → GitHub repo
# → Vercel deployment
# → Live URL in 5 minutes
The trade-off: No visual editor. You have to touch code. But: You have full control. No black box. No limitations.
For me as a developer, that’s the right trade-off. For someone who never wants to write code, WordPress is the better choice.
What I Built That Didn’t Work
The first version had @shared imports.
I thought: Why should I duplicate the same <Button> across 5 templates? I’ll build a @shared/components package, and all templates import from it.
The problem: Generated client projects had dependencies.
A client wanted to self-host. He cloned the project, ran npm install — and got an error. The @shared package wasn’t publicly available. Broken build.
The solution: Templates are fully standalone.
Each template contains all components, all styles, all utils. No external imports. When you generate a client project, it’s a complete, self-contained Astro project. You can push it to GitHub, deploy it to Vercel, host it on your own server — it works everywhere.
The trade-off: Code duplication. The same <Button> exists 5 times. But: Deployment safety. No client will ever have a broken build because a dependency is missing.
That’s the difference between “works on my machine” and “works everywhere”.
Pastelería 2001: Proof It Works in Production
The first real client project was a Mexican bakery in Morelia, Michoacán.
The requirements:
- 28 products in 3 categories (Pan Dulce, Pasteles, Galletas)
- Prices in MXN (Mexican pesos)
- Fully localized (es-MX, Mexican Spanish — no European “vosotros”)
- Mobile-optimized (80% of customers browse on mobile)
- Deployed on Vercel
The implementation:
./create-customer-project.sh
→ Industry: Bakery
→ Market: Mexico (MX)
→ Domain: pasteleria2001.mx
→ Primary color: #D4845A (warm terracotta)
5 minutes later: Live URL, Lighthouse Score 98/100 Performance, fully functional.
What this proved: It’s not just a concept. It’s deployed, it’s running, it has a real customer.
What the System Doesn’t Solve (Yet)
I want to be honest: This is not a CMS.
Limitations:
- No visual editor for clients. Content updates require Git knowledge or my help.
- No e-commerce integration. Product catalogs yes, checkout flow no.
- No user authentication. Static pages, no login areas.
Why that’s okay: The target audience is small businesses that need a presence, not an online shop. Bakeries, craftsmen, consultants, restaurants. They need a website that shows their services, that’s found on Google, that works on mobile.
For everything else, there’s Shopify, WooCommerce, or custom development.
What’s next: CMS integration is planned. Probably Decap CMS (formerly Netlify CMS) — Git-based, no backend, free. But that’s post 5 of this series.
How the Standalone Architecture Prevents Dependency Hell
The next post goes into the technical details: Why standalone templates are better than shared components, how the generator script handles 14 markets with one command, and why Astro’s Content Collections are the perfect data model for template systems.
This article is part 1 of the series “Website Factory — how I built a scalable web business from templates”.
About the author: Daniel is a solopreneur and web developer based in Berlin, operating in the DACH and Latin America markets. He builds systems, not individual projects.