FinOps for Homelabs: Managing Infrastructure and Cloud Costs
Running a homelab is cheap until it isn't. That old Dell PowerEdge you pulled from a dumpster costs nothing to acquire and $40/month to power. The "free tier" VM you spun up on AWS for a quick test is now running 24/7 with 200 GB of forgotten EBS snapshots. The three Raspberry Pis in the closet each draw 5 watts, but the NAS they talk to draws 80.
FinOps — short for Financial Operations — is a discipline that originated in enterprise cloud management. The core idea is simple: make infrastructure costs visible, allocate them to the workloads that generate them, and continuously optimize. You don't need a Fortune 500 budget to benefit from this thinking. A homelab with a few servers, some cloud resources, and a growing electricity bill is the perfect place to start.

This guide covers how to apply FinOps principles to a homelab environment — tracking real electricity costs, optimizing any cloud spending you do alongside your local infrastructure, and building a cost-per-service view so you know exactly where your money goes.
The Three Phases of FinOps
FinOps isn't a tool or a product. It's a practice built on three phases that repeat continuously:
1. Inform — Understand what you're spending and where. You can't optimize what you can't see.
2. Optimize — Identify waste, right-size resources, and make deliberate spending decisions.
3. Operate — Build processes and automation that keep costs under control as your infrastructure grows.
For a homelab, this translates to:
- Inform: Measure power draw per device, track cloud bills, understand per-service cost
- Optimize: Replace power-hungry hardware, consolidate underused VMs, delete forgotten cloud resources
- Operate: Set up alerts, review costs monthly, automate shutdown of non-essential services during off-hours
Tracking Homelab Electricity Costs
Electricity is the largest recurring cost for most homelabs. Unlike cloud billing, there's no dashboard that tells you what each server costs. You have to measure it yourself.
Hardware Power Monitoring
The cheapest and most effective tool is a plug-in power meter. A Kill-A-Watt or TP-Link Kasa smart plug with energy monitoring costs $15-30 and gives you actual watt readings.
# Quick annual cost calculation
# watts * hours_per_year * cost_per_kwh
echo "scale=2; 120 * 8760 * 0.16 / 1000" | bc
# Result: 168.19 (dollars per year for a 120W server at $0.16/kWh)
For multiple devices, use a smart PDU or metered power strip that reports per-outlet consumption:
| Device | Measured Watts | Monthly Cost ($0.16/kWh) | Annual Cost |
|---|---|---|---|
| Proxmox node 1 | 85 W | $9.93 | $119 |
| Proxmox node 2 | 92 W | $10.75 | $129 |
| TrueNAS (4-bay) | 65 W | $7.60 | $91 |
| Network switch | 18 W | $2.10 | $25 |
| Raspberry Pi x3 | 15 W total | $1.75 | $21 |
| UPS overhead | 12 W | $1.40 | $17 |
| Total | 287 W | $33.53 | $402 |
Those numbers might look small individually, but $402/year adds up. That's a new mini PC every year, or a decent 10 GbE switch.
Automated Power Tracking with Smart Plugs
TP-Link Kasa, Shelly, and Tasmota-flashed smart plugs can report energy data to Home Assistant, which gives you historical charts and per-device breakdowns.
# Home Assistant configuration.yaml
# Utility meter for tracking monthly energy per device
utility_meter:
proxmox_node1_monthly:
source: sensor.proxmox_node1_energy
cycle: monthly
truenas_monthly:
source: sensor.truenas_energy
cycle: monthly
total_homelab_monthly:
source: sensor.homelab_total_energy
cycle: monthly
For more advanced setups, export Home Assistant energy data to Prometheus with the prometheus integration and build Grafana dashboards:
# Home Assistant configuration.yaml
prometheus:
namespace: homeassistant
filter:
include_entity_globs:
- sensor.*_energy
- sensor.*_power
Grafana Dashboard for Power Costs
Once power data flows into Prometheus, build a Grafana dashboard with a cost panel:
# Prometheus query: monthly electricity cost per device
# Assumes $0.16/kWh, energy sensor in kWh
sum by (entity_id) (
increase(homeassistant_sensor_kwh_total[30d])
) * 0.16
This gives you a live cost breakdown by device. You can set up alerts when total monthly cost exceeds a threshold:
# Grafana alert rule (in provisioning YAML)
groups:
- name: homelab-cost-alerts
rules:
- alert: HighMonthlyPowerCost
expr: >
sum(increase(homeassistant_sensor_kwh_total[30d])) * 0.16 > 50
for: 1h
labels:
severity: warning
annotations:
summary: "Homelab monthly power cost exceeds $50"
Cloud Cost Tracking
Many homelabbers use cloud services alongside their local infrastructure — a VPS for a public-facing reverse proxy, S3 for offsite backups, a small VM for DNS or monitoring. These costs are easier to track (your cloud provider sends you a bill) but harder to optimize because pricing is complex.
Understanding Your Cloud Bill
Before optimizing, understand the bill structure. Most cloud spending falls into a few categories:
| Category | Examples | Common Waste |
|---|---|---|
| Compute | EC2, Lightsail, DigitalOcean droplets | Oversized instances, forgotten dev VMs |
| Storage | S3, EBS volumes, snapshots | Old snapshots, unattached volumes, wrong storage class |
| Network | Data transfer, load balancers, elastic IPs | Egress charges, unused load balancers |
| DNS/CDN | Route 53, Cloudflare (paid plans) | Usually minimal |
Infracost: Cost Estimation for Infrastructure as Code
If you manage cloud resources with Terraform (and you should), Infracost shows you the cost impact of every change before you apply it.
# Install Infracost
curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh
# Register for a free API key
infracost auth login
# Get a cost breakdown of your Terraform project
cd ~/terraform/homelab-cloud
infracost breakdown --path .
Example output:
Project: homelab-cloud
Name Monthly Qty Unit Monthly Cost
aws_instance.reverse_proxy
Instance usage (t3.micro) 730 hours $7.59
root_block_device
Storage (gp3) 20 GB $1.60
aws_s3_bucket.backups
Standard storage 100 GB $2.30
PUT, COPY, POST requests 1,000 requests $0.01
GET, SELECT requests 10,000 requests $0.00
OVERALL TOTAL $11.50
Integrate Infracost into your CI pipeline to catch cost surprises before they ship:
# .github/workflows/infracost.yml
name: Infracost
on: [pull_request]
jobs:
infracost:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Infracost
uses: infracost/actions/setup@v3
with:
api-key: ${{ secrets.INFRACOST_API_KEY }}
- name: Generate cost diff
run: |
infracost diff --path . \
--compare-to infracost-base.json \
--format json --out-file /tmp/infracost.json
- name: Post PR comment
run: |
infracost comment github \
--path /tmp/infracost.json \
--repo $GITHUB_REPOSITORY \
--pull-request ${{ github.event.pull_request.number }} \
--github-token ${{ secrets.GITHUB_TOKEN }}
OpenCost: Kubernetes Cost Allocation
If your homelab runs Kubernetes (k3s, k8s, or Talos), OpenCost provides per-namespace and per-pod cost breakdowns — even for on-premises clusters.
# Install OpenCost with Helm
helm repo add opencost https://opencost.github.io/opencost-helm-chart
helm install opencost opencost/opencost \
--namespace opencost \
--create-namespace \
--set opencost.customPricing.enabled=true \
--set opencost.customPricing.configmapName=custom-pricing
For homelab use, you need custom pricing since you're not running on a cloud provider. Create a ConfigMap with your actual costs:
# custom-pricing.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: custom-pricing
namespace: opencost
data:
default.json: |
{
"provider": "custom",
"description": "Homelab custom pricing",
"CPU": "0.015",
"RAM": "0.002",
"storage": "0.0005",
"GPU": "0"
}
These numbers come from your actual costs. If your 3-node cluster costs $33/month in electricity and has 24 cores and 96 GB RAM total:
# Per-core-hour cost
echo "scale=6; 33 / 24 / 730" | bc
# 0.001885 per core-hour ≈ $0.015 per core-hour (rounded up for overhead)
# Per-GB-hour RAM cost
echo "scale=6; 33 / 96 / 730" | bc
# 0.000470 per GB-hour ≈ $0.002 per GB-hour (rounded up)
Once OpenCost is running, query it to see cost by namespace:
# Get cost allocation for the last 24 hours
curl -s "http://opencost.opencost:9003/allocation/compute?window=24h&aggregate=namespace" | \
jq '.data[0] | to_entries[] | {namespace: .key, totalCost: .value.totalCost}'
Vantage: Multi-Cloud Cost Visibility
If you use multiple cloud providers (AWS for compute, Backblaze for storage, Cloudflare for CDN), Vantage provides a single dashboard across all of them. The free tier covers up to $2,500/month in cloud spend — more than enough for a homelab.
Connect your accounts, tag your resources, and Vantage shows you:
- Cost trends over time
- Per-service and per-tag breakdowns
- Anomaly detection (sudden cost spikes)
- Right-sizing recommendations
Like what you're reading? Subscribe to HomeLab Starter — free weekly guides in your inbox.
Building a Cost-Per-Service View
The most useful thing you can build is a view that answers: "What does each service I run actually cost me?"
Tag Everything
Whether it's a VM, a container, or a cloud resource, tag it with the service it supports:
# Proxmox VM notes (used as tags)
qm set 101 --description "service:nextcloud,tier:production"
qm set 102 --description "service:plex,tier:production"
qm set 103 --description "service:dev-sandbox,tier:development"
For Docker Compose services, use labels:
# docker-compose.yml
services:
nextcloud:
image: nextcloud:latest
labels:
cost.service: "nextcloud"
cost.tier: "production"
cost.category: "storage"
Calculate Per-Service Cost
Build a simple spreadsheet or script that combines:
- Compute cost: Power draw of the VM/container host, proportional to resource allocation
- Storage cost: Disk space used, priced at your per-TB cost
- Network cost: Any cloud egress or CDN costs
- License cost: If applicable (most homelab software is free)
#!/bin/bash
# Simple per-service cost calculator
# Assumes: total monthly power = $33, total RAM = 96GB, total storage = 20TB
POWER_COST_MONTHLY=33
TOTAL_RAM_GB=96
TOTAL_STORAGE_TB=20
STORAGE_COST_PER_TB=5 # Amortized disk cost per TB per month
calculate_service_cost() {
local service=$1
local ram_gb=$2
local storage_gb=$3
local ram_cost=$(echo "scale=2; $ram_gb / $TOTAL_RAM_GB * $POWER_COST_MONTHLY" | bc)
local storage_cost=$(echo "scale=2; $storage_gb / 1000 * $STORAGE_COST_PER_TB" | bc)
local total=$(echo "scale=2; $ram_cost + $storage_cost" | bc)
printf "%-20s RAM: %4s GB (%5s/mo) Storage: %6s GB (%5s/mo) Total: %6s/mo\n" \
"$service" "$ram_gb" "\$$ram_cost" "$storage_gb" "\$$storage_cost" "\$$total"
}
echo "=== Monthly Cost Per Service ==="
calculate_service_cost "Nextcloud" 4 500
calculate_service_cost "Plex" 8 8000
calculate_service_cost "Home Assistant" 2 20
calculate_service_cost "Gitea" 2 50
calculate_service_cost "Monitoring" 4 100
calculate_service_cost "Pi-hole" 1 2
Hardware Amortization
Don't forget to amortize hardware costs. A $300 mini PC with a 5-year lifespan costs $5/month. A $200 NAS with $400 in drives over 5 years costs $10/month.
Total Cost of Ownership (Monthly) =
(Hardware Purchase Price / Expected Lifespan in Months)
+ Monthly Electricity Cost
+ Cloud Services Cost
+ Replacement Parts Budget (10% of hardware cost annually)
| Item | Purchase | Lifespan | Monthly Amortization | Monthly Power | Total Monthly |
|---|---|---|---|---|---|
| Mini PC (Proxmox) | $300 | 60 mo | $5.00 | $9.93 | $14.93 |
| NAS + Drives | $600 | 60 mo | $10.00 | $7.60 | $17.60 |
| Network switch | $80 | 60 mo | $1.33 | $2.10 | $3.43 |
| UPS | $150 | 36 mo | $4.17 | $1.40 | $5.57 |
| Total | $1,130 | $20.50 | $21.03 | $41.53 |
Budget Alerts and Automation
Cloud Budget Alerts
Every major cloud provider offers budget alerts. Set them up before you need them:
# AWS CLI: Create a budget with email alert at 80% threshold
aws budgets create-budget \
--account-id 123456789012 \
--budget '{
"BudgetName": "homelab-monthly",
"BudgetLimit": {"Amount": "20", "Unit": "USD"},
"TimeUnit": "MONTHLY",
"BudgetType": "COST"
}' \
--notifications-with-subscribers '[{
"Notification": {
"NotificationType": "ACTUAL",
"ComparisonOperator": "GREATER_THAN",
"Threshold": 80
},
"Subscribers": [{
"SubscriptionType": "EMAIL",
"Address": "[email protected]"
}]
}]'
For DigitalOcean, Hetzner, and other providers without native budget APIs, use a cron job that checks your current spend:
#!/bin/bash
# Check DigitalOcean current month balance
BALANCE=$(curl -s -H "Authorization: Bearer $DO_TOKEN" \
"https://api.digitalocean.com/v2/customers/balance" | \
jq -r '.month_to_date_usage')
THRESHOLD=15.00
if (( $(echo "$BALANCE > $THRESHOLD" | bc -l) )); then
echo "WARNING: DigitalOcean spend ($BALANCE) exceeds threshold ($THRESHOLD)" | \
mail -s "Cloud Budget Alert" [email protected]
fi
Automated Cost Reporting
Set up a weekly cost report that aggregates all your infrastructure costs:
#!/bin/bash
# Weekly homelab cost report
# Run via cron: 0 9 * * 1 /home/user/scripts/weekly-cost-report.sh
REPORT_FILE="/tmp/weekly-cost-report.txt"
cat > "$REPORT_FILE" << EOF
=== Homelab Weekly Cost Report ===
Generated: $(date)
ELECTRICITY
Average daily draw: $(cat /var/log/power-monitor/daily-avg.txt) W
Estimated weekly cost: $$(echo "scale=2; $(cat /var/log/power-monitor/daily-avg.txt) * 168 * 0.16 / 1000" | bc)
CLOUD SERVICES
AWS: $$(aws ce get-cost-and-usage \
--time-period Start=$(date -d '7 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--granularity DAILY --metrics BlendedCost | \
jq '[.ResultsByTime[].Total.BlendedCost.Amount | tonumber] | add | . * 100 | round / 100')
TOTAL ESTIMATED WEEKLY: $$(echo "scale=2; ..." | bc)
EOF
cat "$REPORT_FILE"
Optimization Strategies
Right-Size Your Hardware
The biggest single optimization is matching hardware to workload. Common patterns:
| Workload | Overpowered | Right-Sized | Annual Savings |
|---|---|---|---|
| Pi-hole + Unbound | Dedicated server (80W) | Raspberry Pi (5W) | $105 |
| Reverse proxy | Full VM on server (adds 15W) | Runs on router or Pi | $21 |
| Dev environment | Always-on server (100W) | Laptop, start on demand | $140 |
| Media server | Tower server (120W) | Mini PC with GPU (35W) | $119 |
Scheduling Non-Essential Services
Not everything needs to run 24/7. Development VMs, CI runners, and batch processing can be scheduled:
# Cron: Start dev VM at 8am, stop at 6pm on weekdays
0 8 * * 1-5 qm start 103
0 18 * * 1-5 qm shutdown 103
# Cron: Run backup VM only during backup window
0 2 * * * qm start 110
0 5 * * * qm shutdown 110
For cloud resources, use instance schedulers or Lambda functions to stop dev instances outside working hours. A t3.medium running 10 hours/day instead of 24 saves 58% on compute.
Storage Tiering
Not all data needs fast storage. Move cold data to cheaper tiers:
# AWS S3: Lifecycle policy to move old backups to Glacier
aws s3api put-bucket-lifecycle-configuration \
--bucket homelab-backups \
--lifecycle-configuration '{
"Rules": [{
"ID": "archive-old-backups",
"Filter": {"Prefix": "daily/"},
"Status": "Enabled",
"Transitions": [{
"Days": 30,
"StorageClass": "GLACIER_IR"
}, {
"Days": 90,
"StorageClass": "DEEP_ARCHIVE"
}],
"Expiration": {"Days": 365}
}]
}'
Locally, use a tiered storage approach:
- NVMe/SSD: Active VMs, databases, frequently accessed data
- HDD: Media files, backups, archives, infrequently accessed data
- Cold storage: Offsite backup (Backblaze B2 at $0.006/GB/month)
Delete Forgotten Resources
The most common source of waste is resources you forgot about. Monthly, run a cleanup audit:
# AWS: Find unattached EBS volumes
aws ec2 describe-volumes \
--filters "Name=status,Values=available" \
--query 'Volumes[].{ID:VolumeId,Size:Size,Created:CreateTime}' \
--output table
# AWS: Find old snapshots (>90 days)
aws ec2 describe-snapshots --owner-ids self \
--query 'Snapshots[?StartTime<`2025-11-01`].{ID:SnapshotId,Size:VolumeSize,Date:StartTime}' \
--output table
# AWS: Find unused Elastic IPs (they cost $3.60/month if unattached)
aws ec2 describe-addresses \
--query 'Addresses[?AssociationId==null].{IP:PublicIp,AllocationId:AllocationId}' \
--output table
Locally, audit your VMs and containers:
# Proxmox: List all VMs with their status and resource usage
qm list | while read vmid name status mem; do
echo "$vmid $name $status - Last backup: $(ls -lt /var/lib/vz/dump/vzdump-qemu-${vmid}* 2>/dev/null | head -1)"
done
# Docker: Find containers that haven't been started in 30+ days
docker ps -a --format '{{.Names}} {{.Status}}' | grep "Exited"
Monthly Review Checklist
Set a calendar reminder. Once a month, spend 30 minutes on this:
- Review power consumption: Check smart plug data. Did anything spike? Any new devices added?
- Review cloud bills: Check each provider. Any unexpected charges? Any resources to delete?
- Review per-service costs: Update your cost breakdown. Is any service disproportionately expensive for its value?
- Check storage growth: Are any volumes filling up faster than expected?
- Right-size check: Are any VMs using less than 20% of allocated resources consistently? Shrink them.
- Clean up: Delete unused VMs, containers, snapshots, and cloud resources.
FinOps isn't about spending less — it's about spending deliberately. A homelab that costs $50/month and teaches you enterprise skills is a bargain. A homelab that costs $50/month because you forgot to turn things off is waste. The difference is visibility, and that starts with measurement.
