Toen ik SEOJuice ging bouwen, was de eerste echte beslissing geen feature en ook geen prijsmodel. Het was een vraag waar ik steeds op terugkwam: pak ik de stack die ik al door en door ken, of die stack die indrukwekkend staat in een "How We Built It"-blogpost? Ik koos voor saai. En ik zou het zo weer doen, want saaie technologie die je echt begrijpt, verslaat trendy technologie die je onderweg nog moet leren elke keer weer. (Vraag me gerust naar de drie weken die ik kwijt was aan het evalueren van een grafendatabase, voordat ik toegaf dat PostgreSQL gewoon alles kon wat ik nodig had.)
Dit artikel is een open inkijk in elke laag van de infrastructuur van SEOJuice -- niet alleen de uiteindelijke keuzes, maar ook de afwegingen erachter. Als je als founder je eigen stack aan het evalueren bent, hoop ik dat de redenering nuttiger is dan de labels.
Mijn filosofie komt neer op drie regels die ik op de harde manier heb geleerd, na jarenlang side projects te hebben gerund vóór SEOJuice:


Regel 1: Houd zelf controle over het kritieke pad. Als de kernfunctionaliteit van je product afhangt van een third-party API, geef je iemand anders in feite een kill switch. De crawling-engine, linkanalyse en scoring-algoritmes van SEOJuice draaien allemaal op infrastructuur die ik zelf beheer. Zodra je je belangrijkste onderscheidende factor uitbesteedt, ben je geen product meer maar een reseller.
Regel 2: Kies technologie die je om 2 uur 's nachts kunt debuggen. Ik heb FastAPI voor de backend overwogen. Het is sneller in benchmarks, moderner, en de async-aanpak is daar eleganter uitgewerkt. Maar ik schrijf al sinds 2016 in Django. Als Sentry om middernacht afgaat, wil ik niet 40 minuten documentatie zitten lezen -- ik wil meteen weten welke middleware zich misdraagt. Vertrouwdheid is geen luiheid; het is operationele volwassenheid.
Regel 3: Houd het aantal bewegende onderdelen zo klein mogelijk. Elke extra service in je architectuur is weer iets dat stuk kan gaan, gemonitord moet worden en afhankelijkheden heeft die je moet updaten. Ik tel de services in mijn docker-compose-bestand zoals een backpacker grammen telt.
In het hart van SEOJuice draait Django. Ik koos het boven Flask (te kaal voor een product van deze complexiteit), FastAPI (geweldig voor API's, maar ik had Django's admin, ORM en template engine nodig) en Rails (ik ken Ruby niet goed genoeg om het blind te debuggen). Django's "batteries-included"-filosofie betekende dat ik niet de eerste drie maanden kwijt was aan het in elkaar zetten van een authenticatiesysteem, een adminpanel en een migratieframework uit losse pakketten. De keerzijde is dat Django zwaar kan aanvoelen voor simpele microservices -- maar SEOJuice is geen simpele microservice. Het is een monoliet met 15+ apps, en Django kan daar prima mee overweg.
De applicatie draait op ASGI via Uvicorn, met Nginx ervoor als reverse proxy. Ik ben ongeveer een jaar na de start overgestapt van Gunicorn (WSGI) naar Uvicorn, toen ik WebSocket-ondersteuning toevoegde voor realtime notificaties en de MCP-server. Die migratie verliep verrassend soepel -- een van de voordelen van Django's geleidelijke adoptie van ASGI. Cloudflare staat voor alles en regelt DNS, SSL-terminatie en DDoS-bescherming. Ik heb gezien hoe Cloudflare verkeerspieken opving waar mijn servers anders op waren omgevallen, zonder ook maar te zweten.
Alle servers draaien op Linux gehost bij Hetzner. Waarom Hetzner en niet AWS? Eerlijk gezegd: kosten. Een dedicated server bij Hetzner met 64GB RAM kost ongeveer wat een vergelijkbare EC2-instance voor één week kost. Voor een product dat ik zonder externe funding heb opgebouwd, maakt dat verschil enorm veel uit. De keerzijde is minder automatisering -- geen auto-scaling, geen managed Kubernetes. Deployments doe ik met Docker en wat shellscripts. Het is niet glamoureus, maar het werkt, en het geld dat ik bespaar gaat direct terug het product in.
De database is PostgreSQL met de pgvector-extensie. Ik heb MongoDB vroeg in het proces geëvalueerd (iedereen gebruikte het in 2022) en kwam er snel achter dat SEO-data sterk relationeel is -- pagina's horen bij websites, keywords horen bij pagina's, links verbinden pagina's met elkaar. Een documentdatabase had betekend dat ik de helft van wat Postgres gratis levert zelf opnieuw had moeten bouwen. De pgvector-extensie gaf uiteindelijk de doorslag: daarmee kan ik embeddingvectoren direct naast relationele data opslaan en doorzoeken, wat onze matching op contentgelijkenis en AI-features mogelijk maakt zonder een aparte vectordatabase nodig te hebben.
Voor achtergrondtaken gebruik ik Celery met Redis als broker. Celery krijgt veel kritiek in de Python-community, en een deel daarvan is terecht -- de documentatie is versnipperd, de configuratieopties zijn overweldigend en fouten in taken debuggen kan je tot wanhoop drijven. Maar ik heb nog niets beters gevonden voor de pure hoeveelheid achtergrondwerk die SEOJuice doet: duizenden pagina's crawlen, NLP-analyses draaien, rapporten genereren, backlinks monitoren. Ik heb Dramatiq en Huey overwogen als lichtere alternatieven. Dramatiq is oprecht goed, maar het ecosysteem van Celery (beat scheduler, flower monitoring, django-celery-results) gaf voor mij de doorslag.
Authenticatie loopt via Auth0. Voor een eerder project heb ik ooit een eigen authenticatiesysteem gebouwd en ik heb een gênante hoeveelheid tijd verspild aan edge cases rond wachtwoordresets, rate limiting en MFA-flows. Auth0 regelt dat allemaal. De kosten schalen mee met het aantal gebruikers, wat op bepaalde drempels even pijn doet, maar de engineeringtijd die het bespaart is een veelvoud van het abonnement waard.
Alles draait in containers met Docker. Mijn ontwikkelomgeving en productieomgeving draaien identieke containers. Daarmee verdween een hele categorie "works on my machine"-bugs die mijn eerdere projecten teisterden. Sentry monitort exceptions in realtime -- ik kan een door een gebruiker gemelde bug volgen van hun sessie naar de exacte coderegel in minder dan een minuut.
Ik heb heel bewust besloten om React, Vue en elk ander JavaScript-framework te vermijden. Het SEOJuice-dashboard is server-rendered HTML met Tailwind CSS en Alpine.js voor interactiviteit. Dit is de keuze waar ik van andere developers de meeste weerstand op krijg, en ik snap waarom -- het voelt alsof je met handgereedschap bouwt terwijl het elektrische gereedschap al klaar ligt.
Dit is mijn redenering: SEOJuice is een dashboard met veel data. Gebruikers bekijken tabellen, grafieken en rapporten. Ze hebben geen realtime collaborative editing of complexe client-side state management nodig. Server rendering betekent dat elke pagina snel laadt, de initiële payload klein blijft en ik geen aparte frontend-buildpipeline, API-laag of state management library hoef te onderhouden. Alpine.js regelt de dropdowns, modals en interactieve filters. Op de paar plekken waar ik rijkere interactiviteit nodig heb (het realtime monitoring-dashboard), gebruik ik htmx om HTML-fragmenten te wisselen zonder een volledige paginaverversing.
Statische assets lopen via de CDN van Bunny.net. Ik heb de CDN van Cloudflare getest (die ik toch al gebruik voor DNS), maar vond Bunny's prijs per request voorspelbaarder voor mijn verkeerspatronen. Het verschil is klein -- allebei zouden prima werken.
Een flink deel van wat SEOJuice nuttig maakt, is het verwerken van grote hoeveelheden data en het inzetten van AI voor features die handmatig simpelweg niet te bouwen zijn.
Voor taalmodellen integreer ik met OpenAI en Claude. Ik gebruik verschillende modellen voor verschillende taken, gebaseerd op maanden testen: GPT-4o voor gestructureerde data-extractie (het volgt JSON-schema's betrouwbaarder), Claude voor contentanalyse en suggesties (het schrijft natuurlijker en gaat beter om met nuance). Ik heb geprobeerd open-source modellen lokaal te draaien -- Llama 2, daarna Llama 3 -- maar het kwaliteitsverschil voor productiegebruik was nog steeds te groot om de infrastructuurkosten te rechtvaardigen. Die rekensom verandert elke paar maanden, dus ik bekijk dat regelmatig opnieuw.
Ik gebruik pgvector voor zoeken op embeddings in plaats van een gespecialiseerde vectordatabase zoals Pinecone of Weaviate. Embeddingvectoren in PostgreSQL naast de rest van de data hebben betekent dat ik resultaten op embeddinggelijkenis in één SQL-statement kan combineren met relationele queries. De performance is ruim voldoende voor onze schaal. Als ik miljarden vectors draaide, had ik iets specialistisch nodig. Bij miljoenen kan Postgres dit prima aan.
Aan de NLP-kant doen NumPy, NLTK en Scikit-learn het zware werk voor keywordextractie, content scoring en classificatie. Deze libraries zijn niet spannend, maar wel battle-tested. Als ik een TF-IDF-analyse draai over 50,000 pagina's, heb ik een correct resultaat nodig, niet iets innovatiefs.
Voor het crawlen van JavaScript-zware sites gebruik ik Playwright in een aparte container. Dat is resource-intensiever dan simpele HTTP-requests, maar moderne websites renderen kritieke content vaak via JavaScript. Als onze crawler die content niet kan zien, kunnen we die ook niet analyseren. Playwright kan overweg met SPA's, lazy-loaded content en client-side routing die voor een traditionele crawler onzichtbaar zou blijven.
Bij de ondersteunende infrastructuur ben ik het meest bereid geweest om third-party services te gebruiken, omdat geen van deze onderdelen een kernonderscheidende factor is:
Crisp voor live chat. Ik heb eerst Intercom geprobeerd -- te duur en te complex voor een supportteam van één persoon. Crisp doet wat ik nodig heb voor een fractie van de kosten.
Customer.io voor e-mail. Transactionele e-mails (wachtwoordresets, rapportlevering), onboarding-sequenties en productupdates lopen allemaal via Customer.io. Ik ben overgestapt vanaf een simpelere setup (Django's ingebouwde e-mail met SendGrid) omdat ik gedragsgestuurde triggers nodig had -- "stuur een tip-e-mail als de gebruiker zijn WordPress-site niet binnen 3 dagen heeft gekoppeld."
Paddle voor betalingen. Ik begon met Stripe en ben gemigreerd naar Paddle als Merchant of Record. De reden was volledig praktisch: Paddle regelt btw-berekening, inning en afdracht voor elk land. Als Europese founder zonder externe funding die wereldwijd verkoopt, zou het bouwen van mijn eigen tax compliance-systeem een fulltime baan zijn geweest. Paddle pakt een groter deel dan Stripe, maar haalt wel een complete categorie operationele last van mijn bord.
ChartMogul voor omzetanalytics. MRR, churn rate, LTV, extra omzet uit uitbreidingen -- ik heb deze cijfers nodig, accuraat en automatisch bijgewerkt. Ik zou ze uit Paddle's API kunnen berekenen, maar ChartMogul doet het beter en laat trends zien waar ik zelf niet aan zou denken om naar te kijken.
Voor bezoekersanalytics draai ik een self-hosted instance van Plausible. Geen cookies, geen consent banners, geen bezoekersdata naar Google sturen. Het laat me trafficbronnen, toppagina's en referrers zien -- en dat is oprecht alles wat ik nodig heb. (Ik schreef uitgebreider over Plausible in onze gids met open-source SEO-tools.)
Geen enkele stack is perfect, en ik heb inmiddels genoeg geleerd om te weten wat ik anders zou doen als ik opnieuw vanaf nul begon:
Ik zou eerder investeren in Kubernetes. Niet op dag één -- dat is overkill voor een MVP -- maar rond het moment dat ik 10 achtergrondworkers had. Containers orkestreren met shellscripts en systemd werkt, totdat het niet meer werkt, en de overstap naar echte orkestratie terwijl je productieverkeer blijft bedienen is zenuwslopend.
Ik zou de API eerder los trekken van de monoliet. Dat het dashboard, de publieke API, de WordPress-plugin API en de MCP-server allemaal één Django-proces delen, betekent dat een piek in API-verkeer het dashboard kan vertragen. Ik ben die onderdelen geleidelijk aan het loshalen naar aparte services, maar het was netter geweest om daar vanaf het begin al op te ontwerpen.
Wat ik niet zou veranderen, zijn de kernkeuzes -- Django, PostgreSQL, Celery, server-rendered HTML. Die hebben zich bewezen in twee jaar productieverkeer, en de eenvoud die ze opleveren is meer waard dan de performancewinst die ik misschien uit trendier alternatieven had kunnen halen.
Als solo founder is de gevaarlijkste valkuil dat je je stack bouwt voor het Hacker News-publiek in plaats van voor wat je product daadwerkelijk nodig heeft. Elke technologiekeuze die ik hier heb beschreven kwam voort uit een specifieke behoefte, is afgewogen tegen alternatieven en is uiteindelijk gekozen omdat ik er sneller mee kon shippen, met minder verrassingen. Het resultaat is een systeem dat ik in mijn eentje kan draaien, snel kan debuggen en kan uitbreiden zonder alles te herschrijven.
Maar dit is nog maar het begin. De stack evolueert mee met het product -- ik werk continu aan het verbeteren van SEOJuice, en de infrastructuur moet daarin mee. Als jij als founder voor dezelfde keuzes staat, hoop ik dat het zien van de redenering achter mijn keuzes je wat van het trial-and-error bespaart waar ik zelf doorheen ben gegaan.
Laten we blijven bouwen.
Groet,
Vadim
Verder lezen:
no credit card required
No related articles found.