← Blog

Magento

Magento 2 overname: negen maanden naar een dode bucket

Een nachtelijke cron schreef sinds september image-backups naar een verwijderde S3-bucket. De shop draaide door. De CFO wist van niks. Dit deden wij in week één.

Jacob Molkenboer· Oprichter · A Brand New Company· 5 jun 2026· 10 min
Open leren logboek op ivoor papier, koperen vrachtlabel met touw, groen bonnetje, gebarsten rode lakzegel op kaart.

Dag één van een Magento 2-overname begint altijd hetzelfde. Iemand geeft je SSH-credentials in een Notion-doc, de vorige lead engineer is twee weken geleden vertrokken, en de staging URL geeft een 502. Deze was erger dan gemiddeld.

De klant runde een Nederlandse webshop in woonartikelen met drie storefronts op één Magento 2-installatie (één Nederlandse, één Belgische, één Duitse). Gecombineerde omzet rond €4 miljoen per jaar. Stabiele orders, stabiel verkeer, stabiel team. We werden gebeld omdat hun senior developer in maart was opgestapt, zijn vervanger in april, en het bureau waar ze €8k per maand aan betaalden niet meer reageerde op mails.

We kregen root, de AWS-console, en een vage brief: "zorg dat er niets stuk gaat".

Drie dagen later kwamen we erachter dat er al negen maanden iets stuk was.

Wat de codebase eigenlijk was

Magento 2-codebases verouderen als melk. De versie op schijf was 2.4.3-p1. Op dat moment was 2.4.7 actueel. De vendor folder was 1,8GB. Er waren 47 third-party modules, waarvan 12 geforked waren naar app/code zonder enige uitleg waarom.

Het eerste wat we deden was composer show -i draaien en de output in een spreadsheet zetten. Daarna find . -name "*.phtml" -newer composer.lock om elk template te vinden dat met de hand bewerkt was na de laatste composer install. Dat gaf 312 bestanden terug.

Er stonden drie thema's in app/design/frontend. Eén vendor-thema dat met het project meekwam. Eén custom thema genaamd brand-2021 dat ongeveer 40% van de templates overschreef. Eén genaamd brand-2023 dat ongeveer 80% overschreef maar alleen door de Duitse shop werd gebruikt. De Belgische shop viel voor diverse pagina's terug op het parent vendor-thema omdat de override ontbrak. Niemand had het gemerkt, want de pagina in kwestie was de AVG-cookiebanner, die het Belgische team al twee jaar in het Nederlands aan het tonen was.

Dat is het soort dingen dat je vindt bij een Magento-overname. Geen bugs. Drift.

De cron die naar niets schreef

De grotere ontdekking kwam toen we de voor de hand liggende vraag stelden: waar worden de productafbeeldingen geback-upt?

De interim-CTO, die een vriend was van de oprichter en geen fulltime engineer, zei: "er is een nachtelijke cron, die pusht naar S3".

We checkten de cron. Hij draaide elke nacht om 02:15. Laatste geslaagde run, volgens z'n eigen log: gisteren. Geschreven regels: ongeveer 14.000. Exit code: 0. Zag er prima uit.

We openden de S3-console en de bucket was er niet.

Hij was op 4 september van het jaar ervoor verwijderd, negen maanden eerder, door een admin die niet meer bij het bedrijf werkte. We bevestigden dit in CloudTrail. De cron slikte sindsdien stilletjes de fout in, omdat de aws s3 sync-call in een shell script zat dat stderr naar /dev/null pipete en exit'te met de success code van de laatste echo "done" onderaan het bestand.

#!/bin/bash
# nightly-image-backup.sh
cd /var/www/html/pub/media
aws s3 sync . s3://brand-media-backup-2021/ 2>/dev/null
echo "done" # last line. exit 0 always.

Negen maanden lang nul back-ups. Ongeveer 180GB aan opgebouwde productfotografie op één enkel EBS-volume zonder snapshots. We maakten binnen vijftien minuten na de ontdekking een snapshot, en schreven daarna de mail aan de oprichter.

Waarschuwing

Een cron die maandenlang "gewoon werkt" zonder dat iemand de output leest, is een cron die gestopt is met werken. Exit codes uit shell scripts liegen. Controleer altijd wat er daadwerkelijk geproduceerd wordt, niet of de job gedraaid heeft.

De oprichter inlichten

De moeilijkste mail bij een overname is degene waarin je de oprichter vertelt hoe stuk hun spullen zijn zonder dat ze in de verdediging schieten. We hebben er inmiddels genoeg verstuurd om de vorm te kennen.

Het feit in één regel. De blast radius in één regel. Wat je er al aan gedaan hebt in één regel. Bied een belletje van een kwartier aan voor vragen. Geen commentaar op het vorige team.

De mail die we die ochtend stuurden was vier alinea's. De oprichter belde binnen een uur terug, stelde één vraag ("verliezen we op dit moment geld?"), en gaf akkoord op het snapshot-budget. Daarna vroeg hij ons om elke andere cron op de server in kaart te brengen voordat we iets anders deden. Dat instinct was juist.

De triage in week één

Als je een Magento-omgeving overneemt die een jaar op de automatische piloot draait, begin je niet met refactoren. Je begint met loggen. Je gaat ervan uit dat niets in de codebase opzettelijk is en dat niets in de infrastructuur gemonitord wordt.

Dit is de volgorde die wij aanhouden. Hij is ongeveer hetzelfde bij elke overname.

Inventariseer wat er is

Magento, S3, RDS, ElastiCache, de SMTP-relay, de search index, de CDN, de staging box, de home directory van de deploy-user, elke cron in /etc/cron.d en crontab -l voor elke user. Breng het in kaart. Zet het in één document. Noteer wie de credentials beheert en wanneer ze voor het laatst geroteerd zijn.

We vonden twee cron-users waar niemand van wist. Eén draaide een verouderd cleanup-script dat logbestanden ouder dan 7 dagen verwijderde, en daarom hadden we geen logs om de S3-cron mee te onderzoeken.

Zoek wat stilletjes faalt

De cron met de dode bucket was de ergste. Er waren er meer. Een Klaviyo-exportjob die 401 teruggaf sinds de API-key was geroteerd. Een btw-sync van een Belgische belastingdienst die time-outs gaf en terugviel op de laatst gecachte waarde, die acht maanden oud was. Een reindex-cron die wel draaide maar één van de nieuwe productattributen uit 2024 niet meer meenam.

En eentje die we bijna over het hoofd zagen: een double-opt-in webhook stond al drie maanden op een staging URL omdat iemand 'm tijdens een test had ingesteld en nooit had verplaatst. Echte aanmeldingen werden geregistreerd als bounce. Het marketingsysteem toonde "invalid email" voor zo'n 1.400 klanten met prima geldige adressen. De marketing lead voelde aan dat het aantal hoog was. Niemand had het aan de webhook gekoppeld, omdat de webhook in niemand's mentale model van de stack zat.

Je vindt dit soort dingen door elke cron te lezen en je af te vragen: hoe zouden we het weten als dit faalt? In de meeste verouderde omgevingen is het antwoord niets. Niemand zou het weten.

Snapshot alles voordat je iets aanraakt

Volume snapshots, database dump, S3 versioning aan (of opnieuw aanmaken), media folder ge-rsynct naar een aparte regio. Kost ongeveer €40 per maand aan extra opslag. De eerste keer dat er iets misgaat, is dat geld al terugverdiend.

Patch de bekende security-gaten meteen

In 2024 en 2025 bracht Adobe meerdere kritieke Magento-patches uit, waaronder APSB24-40 en de bijbehorende vervolgbulletins. Als de versie op schijf meer dan twee minor releases achterloopt, ga er dan vanuit dat hij kwetsbaar is voor minstens één publiek bekende RCE. Patch voordat je iets anders doet, ook als dat een halve dag regressietesten kost.

Deze klant zat op 2.4.3-p1. We brachten ze binnen drie weken naar 2.4.7-p3, in twee aparte releases. De eerste release waren alleen patches. De tweede was de versiestap. We splitsten het omdat security-werk en feature-werk mengen rollback dubbelzinnig maakt, en bij een overname wil je dat elke rollback één beslissing is, geen discussie.

Kies één thema

Drie thema's is er twee te veel. We diff'ten de twee custom thema's tegen elkaar en tegen het vendor parent-thema. Ongeveer 60% van de overrides in brand-2023 waren kopieën van het vendor-template zonder wijzigingen, overgebleven van een half afgemaakte migratie. Die hebben we verwijderd. Van de echte wijzigingen hielden we de 2023-set en porteerden we de drie bewuste 2021-wijzigingen die nog gewenst waren. Het aantal thema's ging van drie naar één. De Belgische shop werd 240KB lichter, omdat de cookiebanner nu de juiste CSS laadde.

Documenteer de deploy

Het bureau had een deploy. Het was een ongedocumenteerd shell script in /home/deploy/release.sh dat bin/magento-commando's aanriep in een volgorde die meestal werkte. We schreven het om naar een Makefile, committen het naar de repo, en verwijderden het script. Nu kan iedereen in dertig regels lezen wat een deploy doet.

.PHONY: deploy
deploy: pull compile flush cron-restart

pull:
	git fetch && git reset --hard origin/main
	composer install --no-dev --prefer-dist

compile:
	bin/magento setup:upgrade
	bin/magento setup:di:compile
	bin/magento setup:static-content:deploy nl_NL nl_BE de_DE -f

flush:
	bin/magento cache:flush
	bin/magento indexer:reindex

cron-restart:
	sudo systemctl restart magento-cron

Niet spectaculair. Een deploy is één commando en het team kan 'm lezen.

Het heartbeat-patroon dat we nu bij elke cron meeleveren

Na deze overname hebben we een regel ingevoerd: geen geplande job gaat live zonder heartbeat. Het patroon is klein. Elke cron doet z'n werk en stuurt daarna één HTTPS POST naar een gehoste check (we gebruiken Healthchecks.io voor de meeste klanten, een interne endpoint voor klanten met strengere data-regels). Krijgt de check binnen het verwachte tijdvenster geen ping, dan stuurt hij een melding naar een Slack-kanaal dat een mens leest.

#!/bin/bash
set -euo pipefail

# Do the actual work first. Errors propagate because of -e.
cd /var/www/html/pub/media
aws s3 sync . s3://brand-media-backup/

# Only ping success if every command above succeeded.
curl -fsS --retry 3 -m 10 "https://hc-ping.com/<uuid>" > /dev/null

Twee details zijn belangrijk bij de plaatsing van die ping. Het eerste: hij komt na het echte werk, nooit ervoor. We hebben scripts overgenomen die een heartbeat pingen aan het begin van de job, vanuit de gedachte dat het feit dat de cron überhaupt afgaat het belangrijkste is. Dat is niet zo. Wat ertoe doet is dat de job z'n werk gedaan heeft. Het tweede: de strict shell op de eerste regel.

set -euo pipefail is de regel die de stille fout van negen maanden had opgevangen. Bash zonder die regel is de default. Bash met die regel gedraagt zich als een programmeertaal die fouten serieus neemt. We zetten 'm aan in elk script dat we overnemen. Dat breekt een paar dingen bij de eerste deploy. Die dingen waren al stuk. Het script verborg het alleen.

Waarom dit juist bij Magento gebeurt

Magento 2 is een veertien jaar oude PHP-applicatie met een plugin-model dat elk bureau aanmoedigt om de core te forken. Het composer-ecosysteem helpt, maar het upgradepad tussen minor versies raakt nog steeds aan third-party modules op manieren die mensen verrassen. De meeste omgevingen die we zien zijn over hun levensduur door drie tot vijf verschillende bureaus aangeraakt, elk laat artefacten achter.

Het dieperliggende probleem is dat Magento-merchants doorgaans lean operationele teams hebben. Een fulltime engineer in huis is zeldzaam. De shop werkt, orders komen binnen, en de codebase stapelt stilletjes precies dit soort dode crons, verlaten thema's en stille fouten op die we hier vonden.

Adobe heeft aangegeven dat het platform voor nieuwe merchants in onderhoudsmodus zit, terwijl Adobe Commerce steeds meer aan enterprise wordt verkocht. Dat heeft de lange staart van mid-market merchants die tussen 2018 en 2022 op Magento 2 hebben gebouwd niet afgeremd, en zij hebben geen voor de hand liggende plek om naartoe te gaan. Alleen al in Europa zijn dat er tienduizenden.

De merchants die Magento 2 na 2027 blijven draaien, zijn degenen die het platform behandelen als infrastructuur, niet als een project. Dat betekent een onderhoudsbudget op de begroting, een gedocumenteerde inventaris van elke cron en elke fork, en een deploy die elke nieuwe medewerker op dag één kan lezen. Niets daarvan is spectaculair. Alles bij elkaar is het goedkoper dan een replatform van zes maanden die niemand goed begroot heeft, omdat de vorige lead de enige was die wist wat er stuk zou gaan.

Het kleinste wat je vandaag kunt doen

Toen wij deze omgeving overnamen, was de ergste verrassing een cron waarvan de enige faalmodus stilte was. Uiteindelijk hebben we elke geplande job verpakt in een heartbeat-patroon dat bij succes post en bij fouten luid faalt. De meeste van onze process automation-projecten op overgenomen codebases beginnen met precies dit soort audit.

Pak één cron job op je productieserver. Lees de daadwerkelijke output van de afgelopen week. Niet of hij gedraaid heeft. Wat hij heeft opgeleverd. Kun je dat niet zien, dan is dat je eerste ticket.

Kern

Een cron die maandenlang stilletjes gewerkt heeft, is een cron die gestopt is met werken. Exit codes uit shell scripts liegen.

FAQ

Hoe lang duurt het meestal voordat een Magento 2-overname stabiel is?

Voor een middelgrote omgeving: drie tot vier weken om te inventariseren, snapshotten, patchen en consolideren. Refactoren is een aparte, langzamere fase die parallel loopt zodra de omgeving stabiel is.

Moeten we Magento 2 upgraden of migreren naar iets anders?

Blijf op 2.4.x als de orders stabiel zijn en het team het kan onderhouden. Migreren weg van Magento is een traject van zes tot twaalf maanden. De meeste merchants zijn beter af door eerst te patchen en consolideren.

Hoe weten we of onze cron jobs stilletjes falen?

Lees de daadwerkelijke output, niet de exit code. Verpak elke geplande job zodanig dat hij bij succes een heartbeat-service pingt. Is er geen signaal van succes, dan is er ook geen signaal van falen.

Is het veilig om een ongebruikt Magento-thema te verwijderen?

Ja, zodra je het hebt gediffed tegen het actieve thema en bevestigd hebt dat geen enkele storefront er nog op terugvalt. Check de thema-configuratie op store-view-niveau in admin, niet alleen op de default scope.

magentolegacy sitesphpoperationsmigratione-commerce

Iets bouwen?

Start een project