Self-Hosted CRM and Operational Stack for Solopreneurs

Overview

  • Project: Self-Hosted Tool Stack
  • Status: Active — CRM selection in progress
  • Context: Consulting solopreneur, not B2B product sales

CRM Assessment for Consulting

OptionVerdictRationale
EspoCRMBest fitAPI-friendly, custom entities for “Projects”/“Consultations”, service-oriented
KrayinAcceptableLaravel/lightweight, but designed for products — requires “productizing” services
InvoiceNinjaHidden gemClient Manager + Invoicing in one — often replaces CRM entirely for consultants
TwentyOverkillSalesforce alternative; too heavy for solopreneur consulting
Frappe CRMOverkillRarely used alone; designed for ERPNext ecosystem
MonicaRejectedPersonal relationship manager, not for business workflows

Adopted Stack Architecture

Three Layers

  1. Client Data (Source of Truth)

    • EspoCRM: conversations, meeting notes, contract dates
    • InvoiceNinja: tax IDs, billing address, payment status
  2. Execution (The Work)

    • Plane: project delivery, tasks, sprints
    • Peppermint: external client requests and issues
  3. Ledger (The Result)

    • Firefly III: operational costs, subscriptions

Integration via Quartz.NET + C#

No middleware (n8n rejected). Custom Quartz.NET jobs using Dapper (fast reads) and HttpClient (safe writes), with AHK for desktop UI triggers.

Key Workflows

New Client Handshake (EspoCRM to InvoiceNinja)

Lead converted in EspoCRM → Quartz job polls API → creates client in InvoiceNinja → writes invoice_ninja_client_id back to EspoCRM.

Support-to-Project Pipeline

Peppermint ticket tagged #escalate → Quartz job detects → creates Plane issue → replies to ticket with reference.

Revenue Recognition (Get Paid Loop)

InvoiceNinja receives payment → Quartz job formats transaction → POSTs deposit to Firefly III.

Implementation Pitfalls

PitfallRiskFix
Firefly as business toolCan’t generate invoicesUse for expense ledger only; InvoiceNinja for billing
Ghost Peppermint usersUntracked clients create ticketsNightly script checks for unknown emails
InvoiceNinja URL changesClient portal links breakSet final domain before first use
Backup corruptionCopying raw DB folders while runningUse pg_dump/mysqldump inside containers first

Docker Best Practices

  • Create shared solonet Docker network for inter-container communication
  • Keep Postgres (Plane/Peppermint) and MySQL (Espo/InvoiceNinja) databases separate
  • Use Traefik or Nginx Proxy Manager for SSL and routing
  • Plan for SSO (Authentik/Keycloak) even if not implemented immediately