The Problem I Can't Ignore
You know that feeling when you open LinkedIn and see another "I'm humbled to announce..." post? Or someone turning a simple job change into a 12-paragraph hero's journey?
Yeah, me too.
As developers, we're not great at the corporate networking thing. We want to show our work, share our side projects, maybe find some freelance gigs—but without the performative professionalism that makes LinkedIn feel like a never-ending job interview.
So I'm building Brand Builder - a personal branding platform designed specifically for people in tech who want to showcase their work without the corporate cringe.
Current status: Collecting emails for early access. MVP launches in a few weeks. I'm documenting the entire journey.
What It's Going to Be
Think of it as a middle ground between:
- LinkedIn (too corporate, no personality)
- A custom portfolio site (time-consuming, low discoverability)
- Twitter/X (chaotic, not designed for professional showcase)
Planned features for MVP:
- Customizable profile pages with themes that don't look generic
- Achievement tracking (projects, certs, talks, OSS contributions)
- Built-in analytics (who's viewing your profile, where they're coming from)
- Searchable directory so people can actually find you
- One shareable link:
brand-builder.io/username
The Tech Stack (And Why I Chose It)
Next.js 14 + App Router
Going with the App Router because I want to learn it properly while building something real. RSCs are perfect for this - profile pages will be mostly static, so SSG will make them blazing fast.
MySQL + Prisma
I know Postgres is the cool kid, but I'm more comfortable with MySQL from past projects. Prisma gives me the type safety and migration tools I need, and it works beautifully with both.
The schema is designed for scale from day one:
// Key models so far
model User {
id String @id @default(cuid())
email String @unique
emailVerified DateTime?
password String? // Hashed with bcrypt
createdAt DateTime @default(now())
profile Profile?
}
model Profile {
id String @id @default(cuid())
userId String @unique
username String @unique
displayName String
headline String?
bio String? @db.Text
theme String @default("minimal")
viewCount Int @default(0)
achievements Achievement[]
profileViews ProfileView[]
@@index([username])
}
model Achievement {
id String @id @default(cuid())
profileId String
type String // PROJECT, CERTIFICATION, AWARD, etc.
title String
description String? @db.Text
date DateTime
link String?
isFeatured Boolean @default(false)
profile Profile @relation(fields: [profileId], references: [id])
@@index([profileId])
}
NextAuth.js (Auth.js v5)
For authentication. Planning to support:
- Email/password (with verification)
- GitHub OAuth (obvious fit for devs)
- Google OAuth (for broader reach)
Coolify for Deployment
Here's where I'm going against the grain. Instead of Vercel, I'm deploying to a custom VPS using Coolify. Why?
What is Coolify? It's like having your own Heroku/Vercel/Netlify on a VPS. Open source, self-hosted, and handles all the DevOps complexity (SSL, reverse proxy, database management, monitoring) through a nice UI.
Why Coolify over Vercel?
- Cost control - Predictable $20/month VPS vs. unpredictable serverless costs
- Learning - Understand the full stack without manual Nginx/Docker configs
- Control - Direct access to everything (logs, DB, file system) when I need it
- Flexibility - Easy to add Redis, background jobs, cron tasks, etc.
Why Coolify over manual VPS setup?
- SSL certificates automatic (Let's Encrypt)
- Zero-downtime deployments built-in
- Database backups automated
- Monitoring and logs in one place
- GitHub/GitLab integration for auto-deploys
It's the sweet spot between "too complex" (manual Docker/Nginx) and "too abstracted" (serverless platforms where you don't know what's happening).
Deployment Architecture
GitHub Push → Coolify Webhook → Build Docker Image →
Deploy to VPS → Health Check → Switch Traffic
Coolify handles:
- Building the Next.js app
- Running Prisma migrations
- Managing MySQL container
- Reverse proxy with automatic SSL
- Rolling deployments with health checks
I just push to main and it deploys. Simple.
The Interesting Technical Decisions
1. Username Validation That Works as URLs
Usernames become URLs (brand-builder.io/username), so validation is critical. Here's what I landed on:
export const usernameSchema = z
.string()
.min(3, 'Username must be at least 3 characters')
.max(30, 'Username must be less than 30 characters')
.regex(/^[a-z0-9-]+$/, 'Only lowercase letters, numbers, and hyphens')
.regex(/^[a-z0-9]/, 'Must start with a letter or number')
.regex(/[a-z0-9]$/, 'Must end with a letter or number')
.refine((val) => !val.includes('--'), 'No consecutive hyphens')
.refine((val) => !RESERVED_WORDS.includes(val), 'Username not available');
The RESERVED_WORDS array includes things like api, dashboard, admin, etc. to prevent route conflicts. Took me a minute to realize this was necessary—testing saved me from a nasty bug.
2. Privacy-First Analytics Design
I want users to know who's viewing their profile, but without being creepy. My approach:
// Conceptual design - not implemented yet
async function trackProfileView(username: string) {
const headersList = headers();
const ip = headersList.get('x-forwarded-for') || 'unknown';
// Hash the IP + salt for privacy
const hashedIp = crypto
.createHash('sha256')
.update(ip + process.env.ANALYTICS_SALT)
.digest('hex');
// Store only the hash, never the actual IP
await prisma.profileView.create({
data: {
profileId: profile.id,
hashedIp,
userAgent: headersList.get('user-agent'),
referrer: headersList.get('referer'),
viewedAt: new Date()
}
});
}
This gives users analytics (unique views, referrers, trends) without me storing personally identifiable information. Can track unique views by hashed IP, but can't reverse engineer who they are.
3. Markdown Support + Sanitization
Users will write bios and achievement descriptions in Markdown. Great for flexibility, but security is critical:
import { remark } from 'remark';
import html from 'remark-html';
import sanitizeHtml from 'sanitize-html';
export async function parseMarkdown(markdown: string) {
const processed = await remark()
.use(html)
.process(markdown);
return sanitizeHtml(processed.toString(), {
allowedTags: ['p', 'br', 'strong', 'em', 'ul', 'ol', 'li', 'a', 'code', 'pre'],
allowedAttributes: {
'a': ['href', 'title', 'target', 'rel']
},
transformTags: {
'a': sanitizeHtml.simpleTransform('a', {
rel: 'noopener noreferrer',
target: '_blank'
})
}
});
}
Never trust user input. Even from developers. Especially from developers.
4. Theme System Architecture
Planning to launch with 3 themes: Minimal, Bold, and Creative.
The approach: CSS variables + Tailwind classes for easy theming:
// Theme configuration
const themes = {
minimal: {
'--color-primary': '#3B82F6',
'--color-bg': '#FFFFFF',
'--font-family': 'Inter, sans-serif',
'--layout': 'centered'
},
bold: {
'--color-primary': '#EC4899',
'--color-bg': '#F9FAFB',
'--font-family': 'Space Grotesk, sans-serif',
'--layout': 'wide'
},
creative: {
'--color-primary': '#10B981',
'--color-bg': '#0F172A',
'--font-family': 'Poppins, sans-serif',
'--layout': 'asymmetric'
}
};
Users can switch themes from their dashboard, and the public profile updates instantly. Planning to add custom accent color selection too.
What I'm Learning as I Build
1. Scope Creep is Real
I keep wanting to add features. Every day I think "oh, what about X?"
Had to force myself to define MVP clearly:
- ✅ Profiles with achievements
- ✅ 3 themes
- ✅ Basic analytics
- ❌ Social features (later)
- ❌ Custom domains (later)
- ❌ Resume export (later)
Ship first, iterate based on real feedback.
2. Design Takes Longer Than Code
I've spent more time in Figma than VSCode so far. Getting the UX right is crucial—this is a product about presentation, after all.
Still iterating on the profile layouts. Want them to feel personal but not chaotic.
3. MySQL Indexing Strategy
Planning indexes upfront this time (learned the hard way on past projects):
model Profile {
// ...
@@index([username]) // Profile lookups by URL
}
model Achievement {
// ...
@@index([profileId, date]) // Sorted achievement lists
}
model ProfileView {
// ...
@@index([profileId, viewedAt]) // Analytics time-series queries
}
Better to have them from day one than add them when things get slow.
4. Coolify Makes Self-Hosting Actually Viable
Honestly, Coolify was a game-changer. I was ready to deal with Docker Compose files and Nginx configs, but Coolify abstracts all that while still giving me control.
Set up took maybe 30 minutes:
- Spin up VPS (I used OVH, €20/month)
- Install Coolify (one command)
- Connect GitHub repo
- Configure environment variables
- Deploy
Now every push to main auto-deploys. I get logs, metrics, and can easily add Redis or any other service when needed.
If you're considering self-hosting but intimidated by DevOps, check it out.
5. Building in Public is Scary
Putting this out there before it's "perfect" feels weird. But I've seen too many side projects die in private development hell.
So here we are. Imperfect, in-progress, and hopefully useful.
Current Status
What's done:
- ✅ Database schema designed
- ✅ Landing page live at brand-builder.io
- ✅ Waitlist email collection working
- ✅ Core architecture decisions made
- ✅ Design system in Figma
- ✅ Coolify deployment pipeline configured
What's next (next 4-5 weeks):
- 🚧 Authentication system
- 🚧 Profile creation flow
- 🚧 Achievement CRUD
- 🚧 First theme implementation
- 🚧 Analytics tracking
Launch target: Early January, starting with first ~50 users from waitlist.
Why I'm Sharing This
Two reasons:
- Accountability - Public commitment means I actually have to ship
- Learning - You all have way more experience than me. I want your feedback.
I'm not pretending to be an expert. I'm a developer building something I wish existed, and documenting what I learn along the way.
Questions for the Community
Technical:
- Anyone else using MySQL + Prisma? How's the DX compared to Postgres?
- Best practices for profile view analytics that respect privacy?
- Should I self-host images or use S3/R2 from day one?
- Other Coolify users here? What's your experience been?
Product:
- What would actually make you use something like this over LinkedIn?
- What features matter for MVP vs "nice to have later"?
- Would you pay for premium features? What would be worth paying for?
Join the Waitlist (If This Sounds Useful)
If you're tired of LinkedIn or just want a simple way to showcase your tech work without the corporate theater, I'd love your feedback: brand-builder.io
Early users will:
- Get access first (before public launch)
- Help shape what features get built
- Get premium features free for 6 months
No spam, no BS. Just updates on the build and early access when it's ready.
Current tech stack:
- Next.js 14 (App Router)
- React 18 + TypeScript
- MySQL + Prisma ORM
- NextAuth.js (planned)
- Tailwind CSS
- Coolify for deployment
- VPS (Hetzner €5/month)
Building in public:
The real ask: If this sounds interesting, join the waitlist and tell me what you'd actually use. I'm building this for real people with real problems, not for the sake of building.
And if you're building something similar or adjacent, let's connect. Always happy to chat about the journey, share learnings, or just commiserate about scope creep.
P.S. - If you made it this far, here's what I'm wrestling with this week: Should I launch with basic profiles and iterate fast, or nail the theme system first so the first impression is strong? Drop your thoughts below.
P.P.S. - Shoutout to Coolify for making self-hosting accessible. If you're tired of serverless bills but don't want to become a DevOps engineer, check it out.
Top comments (0)