Post

Selfhosting Is Not A Hobby Anymore Its A Way Of Running A Small Business

Selfhosting Is Not A Hobby Anymore Its A Way Of Running A Small Business

Selfhosting Is Not A Hobby Anymore: Its A Way Of Running A Small Business

Introduction

The whir of server fans in a basement office no longer signals just a hobbyist’s playground. What began as simple NAS setups and Docker experiments has evolved into a legitimate operational model for small businesses worldwide. The same infrastructure that once hosted personal media libraries now powers mission-critical business applications, customer-facing services, and entire SaaS alternatives.

This transformation isn’t accidental. Modern selfhosting tools have matured into enterprise-grade solutions, with Kubernetes clusters replacing simple Docker Compose stacks, Proxmox VE managing entire virtualized infrastructures, and Terraform automating cloud-like environments on bare metal. The Reddit user who started with Vaultwarden and evolved to running Forgejo and Invoice Ninja exemplifies this shift – from personal utility to business infrastructure.

For DevOps professionals and sysadmins, this represents both opportunity and responsibility. The skills honed in homelabs now directly translate to cost-effective business operations. This guide will explore:

  1. The technical evolution enabling professional selfhosted infrastructure
  2. Architectural patterns for business-critical selfhosting
  3. Security and compliance considerations
  4. Cost-benefit analysis vs. cloud services
  5. Operational best practices for small business environments

We’ll focus on practical implementation using tools like Docker Swarm, Kubernetes (k3s), Traefik, and enterprise-grade open source applications – all while maintaining the ethos of selfhosted independence.

Understanding Modern Business Selfhosting

From Hobby to Business: The Technical Evolution

Selfhosting crossed a critical threshold when three technological developments converged:

  1. Containerization Maturation: Docker (2013) and Kubernetes (2014) created portable, scalable application packaging
  2. ARM Revolution: Raspberry Pi 4 (2019) and later mini PCs provided enterprise compute at hobbyist prices
  3. Open Source Enterprise Tools: Projects like Nextcloud (2016), Invoice Ninja (2014), and Forgejo (2022) offered commercial-grade alternatives

The practical implications are profound. A $500 mini PC cluster running k3s can now host:

  • Vaultwarden: Enterprise password management (Bitwarden-compatible API)
  • Forgejo: Git hosting with CI/CD pipelines
  • Invoice Ninja: Client billing and payment processing
  • Nextcloud: Collaborative document editing with OnlyOffice integration
  • PeerTube: Video hosting with P2P distribution

Architectural Requirements for Business Selfhosting

Professional selfhosting demands architecture that differs significantly from hobbyist setups:

ComponentHobbyist ApproachBusiness Approach
AvailabilitySingle nodeMulti-node cluster (k3s, Swarm)
BackupsManual rsyncAutomated Velero/Kasten with S3
NetworkingPort forwardingTraefik with Let’s Encrypt wildcard
MonitoringPortainer dashboardPrometheus/Grafana stack
UpdatesAd-hoc docker pullGitOps workflow with FluxCD
AuthenticationIndividual app loginsCentralized Authelia/OAuth2 proxy

Real-World Business Impact

Consider these production scenarios:

Case 1: Consulting Firm Infrastructure

  • Forgejo: Private code repositories with CI/CD
  • Vaultwarden: Team password management
  • Invoice Ninja: Client billing and time tracking
  • Matrix Synapse: Secure client communication
  • Total hardware cost: $1,200Cloud alternative: $600+/month

Case 2: Local Media Publisher

  • MinIO: Private S3-compatible storage
  • WordPress: Content management
  • PeerTube: Video hosting
  • Mastodon: Community engagement
  • Total hardware cost: $2,500Cloud alternative: $1,200+/month

The 6-12 month ROI makes selfhosting financially compelling for small businesses with technical expertise.

Prerequisites for Business Selfhosting

Hardware Requirements

Minimum production-grade hardware:

RoleSpecificationsExample Systems
ControllerQuad-core CPU, 8GB RAM, 128GB SSD, 1Gbps NICIntel NUC 11, Lenovo ThinkCentre
WorkerQuad-core CPU, 16GB RAM, 512GB NVMe, 1GbpsDell Optiplex, HP ProDesk
StorageRAID-capable, ECC RAM, 10Gbps NICSynology DS923+, TrueNAS Mini X+

Software Stack

Core dependencies:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Kubernetes (k3s) - Lightweight production cluster
curl -sfL https://get.k3s.io | sh -s - server \
  --cluster-init \
  --tls-san yourdomain.com \
  --disable traefik
  
# Docker Swarm alternative initialization
docker swarm init --advertise-addr <MANAGER_IP>

# Required utilities
sudo apt-get install -y \
  jq \
  fail2ban \
  wireguard \
  borgbackup \
  nfs-common

Network Architecture

Business-critical selfhosting requires proper network segmentation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Internet
│
├── Reverse Proxy (Traefik/Nginx Proxy Manager)
│   │
│   ├── DMZ Network (10.0.1.0/24)
│   │   ├── Public-Facing Services
│   │   └── TLS Termination
│   │
│   └── Internal Network (10.0.2.0/24)
│       ├── Application Services
│       └── Database Layer
│
├── Management Network (192.168.100.0/24)
│   ├── Kubernetes Control Plane
│   └── Monitoring Stack
│
└── Storage Network (172.16.0.0/24)
    ├── NAS/SAN Devices
    └── Backup Targets

Security Pre-Configuration

Essential pre-deployment security measures:

  1. WireGuard VPN for remote access instead of open ports
  2. Authelia for centralized authentication
  3. Fail2Ban with aggressive jail rules:
    1
    2
    3
    4
    5
    
    [sshd]
    enabled = true
    maxretry = 3
    bantime = 1h
    findtime = 10m
    
  4. Cloudflare Tunnels for DDoS protection without exposing IPs

Installation & Business-Grade Configuration

Kubernetes Cluster Initialization

For production k3s clusters:

1
2
3
4
5
6
7
8
9
10
11
12
13
# First control plane node
curl -sfL https://get.k3s.io | K3S_TOKEN=securetoken sh -s - server \
  --cluster-init \
  --disable=traefik \
  --tls-san yourbusiness.com \
  --node-taint CriticalAddonsOnly=true:NoExecute \
  --kubelet-arg 'rotate-server-certificates=true'

# Additional nodes
curl -sfL https://get.k3s.io | K3S_TOKEN=securetoken sh -s - server \
  --server https://<first-node>:6443 \
  --disable=traefik \
  --node-taint CriticalAddonsOnly=true:NoExecute

Enterprise Application Deployment Patterns

Vaultwarden with High Availability

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# vaultwarden-ha.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vaultwarden
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
  selector:
    matchLabels:
      app: vaultwarden
  template:
    metadata:
      labels:
        app: vaultwarden
    spec:
      containers:
      - name: vaultwarden
        image: vaultwarden/server:latest
        env:
        - name: ADMIN_TOKEN
          valueFrom:
            secretKeyRef:
              name: vaultwarden-secrets
              key: admin_token
        resources:
          limits:
            memory: "512Mi"
            cpu: "0.5"
        livenessProbe:
          httpGet:
            path: /alive
            port: 80
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - vaultwarden
            topologyKey: "kubernetes.io/hostname"

Traefik with Enterprise Features

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# traefik-values.yaml
additionalArguments:
  - "--certificatesresolvers.le.acme.email=admin@yourbusiness.com"
  - "--certificatesresolvers.le.acme.storage=/data/acme.json"
  - "--certificatesresolvers.le.acme.tlschallenge=true"
  - "--entrypoints.websecure.http.tls.options=default@file"
  - "--entrypoints.websecure.http.tls.certresolver=le"
  - "--entrypoints.websecure.http.tls.domains[0].main=yourbusiness.com"
  - "--entrypoints.websecure.http.tls.domains[0].sans=*.yourbusiness.com"

persistence:
  enabled: true
  accessMode: ReadWriteOnce
  size: 128Mi
  storageClass: "local-path"

metrics:
  prometheus:
    enabled: true

deployment:
  replicas: 2

resources:
  limits:
    cpu: "1"
    memory: "512Mi"

Storage Configuration

Business applications require persistent, performant storage:

1
2
3
4
5
6
# Create Longhorn distributed block storage
helm repo add longhorn https://charts.longhorn.io
helm install longhorn longhorn/longhorn \
  --namespace longhorn-system \
  --create-namespace \
  --set defaultSettings.defaultReplicaCount=3

Centralized Authentication

Authelia configuration for unified access control:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# authelia-config.yaml
identity_providers:
  file:
    path: /config/users.yml
    password:
      algorithm: argon2id
      iterations: 3
      salt_length: 16
      parallelism: 4
      memory: 1024

access_control:
  default_policy: deny
  rules:
    - domain: "vaultwarden.yourbusiness.com"
      policy: two_factor
    - domain: "invoices.yourbusiness.com"
      policy: one_factor
    - domain: "*.yourbusiness.com"
      policy: bypass

session:
  name: authelia_session
  secret: # Generate with openssl rand -hex 32
  expiration: 1h
  inactivity: 15m
  domain: yourbusiness.com

Configuration & Optimization for Production

Security Hardening Checklist

  1. Pod Security Policies (Kubernetes):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    apiVersion: policy/v1beta1
    kind: PodSecurityPolicy
    metadata:
      name: restricted
    spec:
      privileged: false
      allowPrivilegeEscalation: false
      requiredDropCapabilities:
        - ALL
      volumes:
        - 'configMap'
        - 'emptyDir'
        - 'persistentVolumeClaim'
      hostNetwork: false
      hostIPC: false
      hostPID: false
      runAsUser:
        rule: 'MustRunAsNonRoot'
      seLinux:
        rule: 'RunAsAny'
      supplementalGroups:
        rule: 'MustRunAs'
        ranges:
          - min: 1
            max: 65535
      fsGroup:
        rule: 'MustRunAs'
        ranges:
          - min: 1
            max: 65535
    
  2. Network Policies (Zero Trust): ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all spec: podSelector: {} policyTypes:
    • Ingress
    • Egress ```
  3. Automated Vulnerability Scanning:
    1
    2
    3
    4
    5
    6
    
    # Install Trivy operator
    helm repo add aqua https://aquasecurity.github.io/helm-charts/
    helm install trivy-operator aqua/trivy-operator \
      --namespace trivy-system \
      --create-namespace \
      --set="trivy.ignoreUnfixed=true"
    

Performance Optimization

Database Tuning for Selfhosted Apps

PostgreSQL optimization for Invoice Ninja:

1
2
3
4
5
6
7
8
9
10
11
12
13
# postgresql.conf
shared_buffers = 2GB
effective_cache_size = 4GB
work_mem = 32MB
maintenance_work_mem = 512MB

max_parallel_workers_per_gather = 2
max_worker_processes = 4
max_parallel_workers = 4

wal_buffers = 16MB
random_page_cost = 1.1
effective_io_concurrency = 200

Resource Quotas for Kubernetes

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: ResourceQuota
metadata:
  name: business-apps
spec:
  hard:
    requests.cpu: "8"
    requests.memory: 16Gi
    limits.cpu: "16"
    limits.memory: 32Gi
    requests.storage: 100Gi
    persistentvolumeclaims: "10"

Backup Strategy

Enterprise-grade backup with Kasten K10:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Install Kasten
helm repo add kasten https://charts.kasten.io/
helm install k10 kasten/k10 \
  --namespace=kasten-io \
  --create-namespace \
  --set global.persistence.size=20Gi \
  --set auth.tokenAuth.enabled=true

# Create backup policy
cat <<EOF | kubectl apply -f -
apiVersion: config.kio.kasten.io/v1alpha1
kind: Policy
metadata:
  name: business-critical-backup
spec:
  frequency: "@hourly"
  retention:
    hourly: 24
    daily: 7
    weekly: 4
    monthly: 12
    yearly: 3
  actions:
    - action: backup
  selector:
    matchExpressions:
      - key: k10.kasten.io/appNamespace
        operator: In
        values: ["vaultwarden", "invoiceninja"]
EOF

Operational Management

GitOps Workflow with FluxCD

Automated infrastructure management:

1
2
3
4
5
6
7
# Bootstrap Flux
flux bootstrap github \
  --owner=yourbusiness \
  --repository=infrastructure \
  --branch=main \
  --path=./clusters/production \
  --personal

Sample cluster structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
infrastructure/
└── clusters/
    └── production/
        ├── apps/
        │   ├── vaultwarden/
        │   │   └── kustomization.yaml
        │   └── invoiceninja/
        │       └── kustomization.yaml
        ├── infrastructure/
        │   ├── longhorn/
        │   └── traefik/
        └── flux-system/
            ├── gotk-components.yaml
            └── gotk-sync.yaml

Monitoring Stack

Production-grade observability:

1
2
3
4
5
6
7
# Install Prometheus Stack
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --create-namespace \
  --set grafana.adminPassword='securepassword' \
  --set prometheus.prometheusSpec
This post is licensed under CC BY 4.0 by the author.