Obsidian Cost Tuning
Overview
Optimize costs across Obsidian's paid services and third-party plugin API usage. Covers Obsidian Sync storage management ($4-$10/mo), Publish hosting optimization ($8/mo per site), vault size reduction strategies, plugin API cost control with caching and quotas, and self-hosted alternatives for zero-cost sync.
Prerequisites
- Understanding of your Obsidian subscription tier
- Terminal access to the vault directory
- Knowledge of which community plugins make external API calls
Cost Structure
| Service | Price | Storage | Cost Driver |
|---|
| Obsidian (core) | Free | N/A | None |
| Catalyst (early access) | $25 one-time | N/A | One-time |
| Sync (Standard) | $4/mo | 1 GB | Vault size, attachment count |
| Sync (Plus) | $8/mo | 10 GB | Large vaults with media |
| Publish | $8/mo per site | N/A | Published page count, bandwidth |
| Plugin API costs | Varies | N/A | Per-call pricing (AI, translation, etc.) |
Instructions
Step 1: Audit Vault Size and Storage Usage
set -euo pipefail
VAULT_PATH="${1:-$HOME/MyVault}"
echo "=== Vault Storage Audit ==="
echo "Total vault size: $(du -sh "$VAULT_PATH" 2>/dev/null | cut -f1)"
echo ".obsidian size: $(du -sh "$VAULT_PATH/.obsidian" 2>/dev/null | cut -f1)"
echo ""
echo "=== Files by Type ==="
command find "$VAULT_PATH" -type f -not -path '*/.obsidian/*' -not -path '*/.trash/*' \
| sed 's/.*\.//' | sort | uniq -c | sort -rn | head -15
echo ""
echo "=== Top 20 Largest Files ==="
command find "$VAULT_PATH" -type f -not -path '*/.obsidian/*' \
-exec du -h {} + 2>/dev/null | sort -rh | head -20
echo ""
echo "=== Plugin Cache Sizes ==="
for dir in "$VAULT_PATH/.obsidian/plugins"/*/; do
[ -f "$dir/data.json" ] || continue
size=$(du -h "$dir/data.json" 2>/dev/null | cut -f1)
echo " $(basename "$dir")/data.json: $size"
done
Step 2: Reduce Sync Storage — Exclusion Patterns
Obsidian Sync respects .obsidian/sync-exclude.json for excluding paths:
{
"patterns": [
"*.pdf",
"*.mp4",
"*.mov",
"*.zip",
"*.tar.gz",
"attachments/archives/**",
"node_modules/**",
".git/**"
]
}
For manual file-level control:
command find "$VAULT_PATH" -type f -size +5M -not -path '*/.obsidian/*' \
-exec du -h {} + | sort -rh
echo "PDF count: $(command find "$VAULT_PATH" -name '*.pdf' | wc -l)"
echo "Image count: $(command find "$VAULT_PATH" \( -name '*.png' -o -name '*.jpg' -o -name '*.jpeg' \) | wc -l)"
echo "Total attachments: $(du -sh "$VAULT_PATH/attachments" 2>/dev/null | cut -f1)"
Strategies to stay under the 1 GB Sync Standard tier:
- Move PDFs to a local folder outside the vault, link with ` URIs
- Compress images before adding:
pngquant --quality=65-80 *.png or ImageOptim
- Use external image hosting (Cloudinary free tier: 25 credits/mo, ~25K transforms)
- Exclude
.obsidian/plugins/*/data.json — plugin caches regenerate on launch
Step 3: Optimize Plugin API Costs
Plugins that call external APIs (AI assistants, translation, image generation) can incur per-call costs. Implement caching in your plugin:
import { Plugin } from 'obsidian';
interface CacheEntry<T> {
result: T;
timestamp: number;
}
export class APICache<T> {
private cache = new Map<string, CacheEntry<T>>();
private ttlMs: number;
constructor(ttlMinutes: number = 60) {
this.ttlMs = ttlMinutes * 60 * 1000;
}
get(key: string): T | null {
const entry = this.cache.get(key);
if (!entry) return null;
if (Date.now() - entry.timestamp > this.ttlMs) {
this.cache.delete(key);
return null;
}
return entry.;
}
() {
..(key, { result, : .() });
(.. > ) {
oldest = ..().().;
(oldest) ..(oldest);
}
}
(: , : <T>): <T> {
cached = .(key);
(cached !== ) cached;
result = ();
.(key, result);
result;
}
() { ..(); }
() { ..; }
}
aiCache = <>( * );
(): <> {
hash = (noteContent);
aiCache.(hash, () => {
response = ({ : , ... });
response..[]..;
});
}
Step 4: Rate Limiting for External Calls
class APIQuota {
private calls = 0;
private resetTime = 0;
private maxCallsPerHour: number;
constructor(maxPerHour: number) {
this.maxCallsPerHour = maxPerHour;
}
canCall(): boolean {
const now = Date.now();
if (now - this.resetTime > 3600000) {
this.calls = 0;
this.resetTime = now;
}
return this.calls < this.maxCallsPerHour;
}
recordCall() { this.calls++; }
remaining(): number {
return Math.max(0, this.maxCallsPerHour - this.calls);
}
}
const quota = ();
() {
(!quota.()) {
();
;
}
quota.();
}
Step 5: Optimize Obsidian Publish Costs
Publish at $8/mo per site. Minimize what you publish to reduce bandwidth:
---
publish: true
permalink: custom-url
---
Cost reduction strategies:
- Use
publish: true frontmatter selectively instead of publishing entire folders
- Compress images before embedding (target < 200KB per image)
- Use lazy-loading for heavy media:
!alt with external hosting
- Monitor page count — each additional page adds build time and bandwidth
- Use Obsidian's built-in image compression in Publish settings
Step 6: Self-Hosted Sync Alternatives (Free)
obsidian_git:
cost: Free
setup: Install Obsidian Git plugin, configure repo
pros: Full version history, unlimited storage, branch per device
cons: Manual setup, no conflict resolution UI, requires Git knowledge
best_for: Developers, technical users
syncthing:
cost: Free
setup: Install on each device, share vault folder
pros: Real-time sync, no cloud dependency, encrypted
cons: Devices must be online simultaneously
Step 7: Ongoing Cost Monitoring Script
#!/bin/bash
VAULT="${1:-$HOME/MyVault}"
echo "=== Monthly Cost Estimate ==="
SIZE_MB=$(du -sm "$VAULT" 2>/dev/null | cut -f1)
echo "Vault size: ${SIZE_MB} MB"
if [ "$SIZE_MB" -lt 1024 ]; then
echo "Sync tier needed: Standard ($4/mo) — under 1 GB"
elif [ "$SIZE_MB" -lt 10240 ]; then
echo "Sync tier needed: Plus ($8/mo) — under 10 GB"
else
echo "WARNING: Vault exceeds 10 GB — Sync Plus limit"
fi
PUB_COUNT=$(grep -rl 'publish: true' "$VAULT"/*.md "$VAULT"/**/*.md 2>/dev/null | wc -l)
echo "Published pages: $PUB_COUNT"
[ "$PUB_COUNT" -gt 0 ] && echo "Publish cost: \$8/mo" || echo "Publish cost: \$0"
echo
/*/;
manifest=
[ -f ] ||
name=$(python3 -c 2>/dev/null)
grep -q 2>/dev/null;
Output
- Vault storage audit with file type breakdown and largest files
- Sync exclusion patterns reducing bandwidth consumption
- API response cache with TTL and bounded size
- Rate limiter preventing runaway API costs
- Publish optimization with selective frontmatter and image compression
- Self-hosted sync comparison (Git, Syncthing, iCloud, Remotely Save)
- Monthly cost estimation script
Error Handling
| Issue | Cause | Solution |
|---|
| Sync storage full | Large binary attachments | Exclude PDFs/videos from sync, compress images |
| Plugin API costs spike | No caching or rate limiting | Implement APICache + APIQuota (Steps 3-4) |
| Vault backup too large | Accumulated .trash and plugin caches | Empty .trash/, exclude plugins/*/data.json from sync |
| Publish site slow | Large uncompressed images | Compress to < 200KB, use external image CDN |
| Git sync conflicts on .obsidian/ | Multiple devices editing config | Add workspace.json to .gitignore |
| iCloud sync corruption | Simultaneous edits on two Apple devices | Never edit same note on two devices at once |
Examples
Quick Storage Savings
command find "$VAULT_PATH" -name '*.png' -exec pngquant --quality=65-80 --skip-if-larger --ext .png --force {} \;
mkdir -p ~/VaultArchive
command find "$VAULT_PATH" -name '*.pdf' -size +10M -exec mv {} ~/VaultArchive/ \;
Persistent API Cache Across Sessions
async onload() {
const data = await this.loadData();
if (data?.apiCache) this.apiCache.restore(data.apiCache);
}
onunload() {
this.saveData({ ...this.settings, apiCache: this.apiCache.serialize() });
}
Resources
Next Steps
For performance optimization, see obsidian-performance-tuning.
For data backup and recovery patterns, see obsidian-data-handling.