Le projet en une phrase
Transformer un MacBook Pro M3 réinitialisé en serveur maison 24/7, dédié à faire tourner des applications Docker personnelles, accessibles en local et à distance via un VPN privé, le tout configuré en une après-midi depuis un poste Kubuntu.
Le serveur a été baptisé higgins, en référence à une convention de nommage personnelle. Le résultat est un homelab opérationnel, sans VPS payant, sans port ouvert sur la box internet, sans solution clé en main lourde.
Le contexte de départ
- Profil développeur fullstack freelance, pas administrateur système
- Habitude quotidienne de Kubuntu, aucune expérience préalable de macOS en mode serveur
- Un MacBook Pro M3 qui dormait dans un tiroir après reset usine
- Besoin concret : héberger un scraper LinkedIn (Node.js, Express, SQLite) et son interface Nuxt pour de la prospection freelance, avec un workflow de déploiement rapide depuis le poste de dev
L'objectif n'était pas de monter un datacenter domestique, mais d'arrêter de payer des VPS et de cesser de mélanger prod et dev sur le poste principal.
La philosophie du setup
Trois principes ont guidé chaque choix technique.
- Tout en ligne de commande depuis Kubuntu. Le Mac est une boîte dans un coin, jamais touchée physiquement après la configuration initiale. Clavier et écran externes sont inutiles.
- Pas de solution clé en main payante. Pas de n8n, pas de Portainer, pas de Coolify. Juste Docker, Compose, quelques scripts bash et de la rigueur.
- Chaque galère debuggée manuellement. Comprendre pourquoi un service casse, plutôt que copier-coller une recette. Le temps investi en compréhension se rembourse sur la durée.
La couche système : macOS en mode serveur
macOS n'a pas été pensé pour servir 24/7. Plusieurs réglages sont nécessaires pour en faire un serveur fiable.
- Hostname renommé en higgins pour retrouver la machine facilement sur le réseau
- Mode "no sleep" via la commande
pmsetpour que le Mac ne se mette jamais en veille sur secteur - LaunchDaemon caffeinate installé en tant que daemon système permanent, pour empêcher toute mise en veille même capot fermé
- Auto-login activé et FileVault désactivé, pour permettre un redémarrage totalement autonome après une coupure de courant
- Lock screen désactivé, sans ça certains services devenaient inaccessibles après cinq minutes de capot fermé
- Réglages tcpkeepalive et networkoversleep pour maintenir les connexions réseau actives en permanence
- SSH activé avec authentification par clé publique uniquement, aucun mot de passe autorisé
Ces paramètres pris ensemble transforment le Mac en machine qui ne dort jamais, redémarre seule après coupure et accepte uniquement les connexions sécurisées.
La couche réseau : Tailscale plutôt que ports ouverts
C'est l'arbitrage qui change tout. Plutôt qu'ouvrir des ports sur la box internet (avec tous les risques associés), un VPN mesh privé a été retenu.
- Tailscale installé comme daemon système : VPN privé, aucun port exposé publiquement
- MagicDNS Tailscale : higgins accessible par son nom depuis n'importe où, domicile, 4G, café ou déplacement
- mDNS (Bonjour) exploité pour un accès en LAN sans Tailscale via
higgins.local - Clé SSH dédiée (higgins_ed25519) générée avec passphrase, ajoutée à
~/.ssh/configcôté Kubuntu pour taper simplementssh higgins
Résultat concret : le serveur est joignable depuis n'importe où, sans aucune configuration de routeur, sans DNS dynamique, sans certificat à gérer. Un laptop en déplacement ou un téléphone en 4G accèdent à higgins de la même manière qu'une machine du salon.
La couche runtime : OrbStack plutôt que Docker Desktop
Sur Apple Silicon, Docker Desktop est lourd. OrbStack est une alternative native, conçue pour M1, M2 et M3, nettement plus légère en RAM et en CPU.
- OrbStack installé comme runtime Docker principal
- Docker 28 et Compose v2 opérationnels, builds ARM64 natifs
- Désactivation de l'auto-pause des containers OrbStack, un piège par défaut qui suspendait les services au bout de quelques minutes d'inactivité
- Réseau Docker partagé homelab, créé en external, pour que les containers se parlent par leur nom de service (scraper-front appelle scraper-back, scraper-back écrit dans mailhog)
L'auto-pause est un piège particulièrement vicieux : les containers semblent démarrés mais ne répondent plus après quelques minutes sans trafic. Sur un serveur, cette optimisation devient un bug à désactiver.
La stack applicative déployée
Scraper-back : Express, TypeScript, better-sqlite3
- Dockerfile multi-stage (builder et runtime distincts)
- Image finale autour de 560 Mo, compilée en ARM64 natif sur higgins
- Volumes persistants pour la base SQLite, les templates email et les rapports
- Healthcheck
/healthintégré dans le conteneur - API REST et CLI multi-commandes : search, search-companies, search-people, search-jobs, send-campaign, import-xlsx
Scraper-front : Nuxt 4 SPA et TypeScript
- Dockerfile multi-stage, runtime basé sur
.output/généré par Nitro - Communique avec le backend via le DNS Docker interne (
http://scraper-back:3001) et non via l'IP publique - Accessible en navigateur sur
http://higgins:3000depuis Kubuntu
Mailhog : capture SMTP pour dev et test
- Container officiel, interface web sur le port 8025, SMTP sur le port 1025
- Tous les containers backend pointent vers
mailhog:1025pour intercepter les emails de test - Évite de polluer des vraies inbox pendant les tests de campagnes email
La couche déploiement : une commande, dix secondes
Le workflow de déploiement est la partie la plus travaillée. Deux scripts bash dans ~/bin/ côté Kubuntu, baptisés deploy-back et deploy-front.
Un déploiement complet suit ces étapes :
- rsync du code local vers higgins, avec exclusion stricte des fichiers sensibles (
.env,data/,node_modules,.git) - Rebuild Docker avec cache, uniquement les couches impactées sont reconstruites
- Restart du container concerné via
docker compose up -d - Auto-restart du front dans le script
deploy-back, pour forcer la réinitialisation des connexions Docker internes
Deux points techniques ont demandé un peu de travail. Le PATH docker est vide en SSH non-interactif, d'où l'usage systématique du chemin absolu /usr/local/bin/docker dans les scripts. Et un rebuild du backend casse la résolution DNS côté front, d'où le restart automatique pour éviter l'erreur.
Temps de déploiement mesuré : entre dix et trente secondes avec cache Docker intelligent. Suffisamment court pour déployer sans réfléchir, ce qui est la vraie métrique qui compte.
La couche dev et ops : rigueur du quotidien
- SQLite accessible depuis higgins et depuis Kubuntu, via un transfert rsync ponctuel de la base pour du dev local
- Templates emails montés en volume : modification à chaud sans rebuild
- Cheatsheet Markdown centralisée couvrant accès, Docker, SQLite, déploiement, dépannage, chemins. Mise à jour en temps réel à chaque apprentissage
- Règle absolue : toujours éditer côté Kubuntu, higgins ne doit être qu'un miroir, jamais une source
Les galères rencontrées et leurs solutions
Un homelab qui fonctionne du premier coup n'existe pas. Voici les pièges rencontrés, chacun résolu méthodiquement.
- OrbStack suspend les containers en idle : désactivation dans les settings de l'application
- Le Mac verrouille la session après cinq minutes de capot fermé : désactivation via
defaultset dans l'interface graphique - Permissions SQLite cassées dans le volume Docker : suppression de la directive
USER nodedans le Dockerfile (OrbStack sur macOS ignore les permissions host) - Variables d'environnement non rechargées au
docker compose restart: il fautdownpuisup -dpour que le.envsoit relu - PATH docker vide en SSH non-interactif : chemin absolu
/usr/local/bin/dockerdans les scripts bash - rsync qui écrase un fichier modifié directement sur higgins : règle établie, toujours éditer côté Kubuntu
- scraper-front qui ne voit plus scraper-back après rebuild : restart du front dans le script
deploy-backpour forcer la réinitialisation DNS Docker
Ce que permet ce setup concrètement
- Développer en local sur Kubuntu, déployer en prod en une commande
- Accéder au serveur depuis n'importe où, téléphone en 4G ou laptop en déplacement, sans ouvrir de ports
- Faire tourner plusieurs applications sans conflits grâce à l'isolation Docker
- Intercepter les emails de test sans polluer de vraies inboxes
- Environnement reproductible, tout est codé dans des Dockerfile, Compose et scripts, pas de configuration cachée dans une tête
- Coût mensuel : zéro euro, contre vingt à cinquante euros pour un VPS équivalent
- Ressources utilisées : environ quatre gigaoctets de RAM, cinq gigaoctets d'espace disque, consommation électrique négligeable (le M3 est très efficient)
Les chiffres clés du setup
- Une après-midi pour la configuration initiale complète
- Dix à trente secondes pour un déploiement complet avec cache Docker
- Environ 560 Mo pour l'image Docker du backend multi-stage optimisé
- Zéro port ouvert sur la box internet
- Zéro euro par mois de coût d'hébergement
Ce qui reste à faire
Un homelab est un terrain évolutif. Les prochaines étapes identifiées :
- Reverse proxy Caddy pour des URLs propres,
scraper.higgins.localplutôt quehiggins:3000 - HTTPS via Tailscale certs ou Let's Encrypt avec DNS
- Backups automatiques et versionnés de la base SQLite
- Monitoring et healthcheck externe via Healthchecks.io en dead-man's switch
- CI/CD GitHub Actions pour automatiser tests, build et déploiement sur push
L'arborescence /opt/homelab/ est prête à accueillir d'autres applications (un dartscore, un gestionnaire de fichiers), sans refonte.
Les enseignements du week-end
Un MacBook qui dormait dans un tiroir est devenu un serveur personnel permanent en une après-midi. Pas de VPS à payer, pas de port exposé publiquement, pas de solution clé en main à maintenir. Juste les bons outils assemblés avec méthode : Tailscale pour le réseau, OrbStack pour le runtime, Docker Compose pour l'orchestration, rsync et bash pour le déploiement.
La leçon principale porte sur la différence entre apprendre à configurer et copier-coller une recette. Chaque piège a été compris avant d'être contourné, ce qui rend l'ensemble maintenable dans six mois, un an, deux ans. Un homelab n'a de valeur que s'il tient dans la durée.
Un projet d'infrastructure, de déploiement ou d'outillage interne ? Prenons contact. Setups pragmatiques, sans dépendance à des SaaS payants lorsque ce n'est pas nécessaire.

