Skip to main content

Open-source alternatives guide

Self-Host Ghostfolio: Investment Portfolio Tracker 2026

Self-host Ghostfolio in 2026. AGPL 3.0, ~4K stars, TypeScript — privacy-first investment portfolio tracker with multi-currency support and benchmarking.

·OSSAlt Team
Share:

TL;DR

Ghostfolio (AGPL 3.0, ~4K GitHub stars, TypeScript/Angular) is a privacy-first investment portfolio tracker. Track stocks, ETFs, crypto, and other assets across multiple accounts and currencies — all on your own server. Personal Capital (now Empower) requires linking your brokerage accounts to their cloud; Ghostfolio keeps everything local. Multi-currency support, dividend tracking, benchmark comparison, and a clean UI for understanding your net worth.

Key Takeaways

  • Ghostfolio: AGPL 3.0, ~4K stars, TypeScript — investment portfolio tracker
  • Multi-currency: Track assets in any currency with automatic conversion
  • Data providers: Yahoo Finance, Alpha Vantage, and more for real-time quotes
  • Benchmarking: Compare portfolio performance against S&P 500, custom indices
  • Dividend tracking: Track dividend income, yield, and reinvestment
  • Privacy-first: No brokerage account linking — manual or CSV import only

Part 1: Docker Setup

# docker-compose.yml
services:
  ghostfolio:
    image: ghostfolio/ghostfolio:latest
    container_name: ghostfolio
    restart: unless-stopped
    ports:
      - "3333:3333"
    environment:
      NODE_ENV: production
      HOST: "0.0.0.0"
      PORT: 3333
      
      # Database:
      DATABASE_URL: "postgresql://ghostfolio:${DB_PASSWORD}@db:5432/ghostfolio"
      
      # Redis:
      REDIS_HOST: redis
      REDIS_PORT: 6379
      
      # Security:
      ACCESS_TOKEN_SALT: "${ACCESS_TOKEN_SALT}"   # openssl rand -hex 32
      JWT_SECRET_KEY: "${JWT_SECRET}"              # openssl rand -hex 32
      
      # Data providers (at least one required):
      # Yahoo Finance (free, default):
      # No additional config needed
      
      # Alpha Vantage (free tier: 25 req/day):
      # ALPHA_VANTAGE_API_KEY: "your-key"
      
    depends_on:
      - db
      - redis

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    volumes:
      - ghostfolio_db:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: ghostfolio
      POSTGRES_PASSWORD: "${DB_PASSWORD}"
      POSTGRES_DB: ghostfolio

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    volumes:
      - ghostfolio_redis:/data

volumes:
  ghostfolio_db:
  ghostfolio_redis:
echo "DB_PASSWORD=$(openssl rand -base64 24)" >> .env
echo "ACCESS_TOKEN_SALT=$(openssl rand -hex 32)" >> .env
echo "JWT_SECRET=$(openssl rand -hex 32)" >> .env

docker compose up -d

Visit http://your-server:3333 → create your account.

Part 2: HTTPS with Caddy

portfolio.yourdomain.com {
    reverse_proxy localhost:3333
}

Part 3: Adding Accounts and Holdings

Create accounts

Accounts represent your brokerage, bank, or crypto accounts:

  1. Accounts → + Add Account
  2. Name: Fidelity, Vanguard, Coinbase, 401k
  3. Platform: select or create
  4. Currency: USD, EUR, GBP, etc.

Add holdings

  1. Portfolio → + Add Activity
  2. Activity type: Buy / Sell / Dividend
  3. Search for asset: AAPL, VTI, BTC
  4. Fill in:
    • Date: purchase date
    • Quantity: number of shares/units
    • Unit price: price per share at purchase
    • Fee: transaction fee
    • Account: which account
  5. Save

Supported asset types

TypeExamples
StocksAAPL, MSFT, GOOGL
ETFsVTI, VOO, QQQ, VT
CryptoBTC, ETH, SOL
Mutual FundsVTSAX, FXAIX
BondsTreasury, Corporate
Precious MetalsGold, Silver
CashSavings accounts, CDs

Part 4: Portfolio Dashboard

Overview

The dashboard shows:

  • Total net worth: Sum of all accounts
  • Performance: Absolute and percentage returns
  • Allocation: By asset class, region, sector, account
  • Holdings table: Each position with current value, gain/loss, weight

Performance chart

  • Time-weighted return vs money-weighted return
  • Compare against benchmarks (S&P 500, total market)
  • Filter by: 1M, 3M, 6M, YTD, 1Y, 5Y, Max

Allocation view

  • By asset class: Stocks vs bonds vs crypto vs cash
  • By region: US, Europe, Emerging Markets
  • By sector: Technology, Healthcare, Finance, etc.
  • By account: Distribution across brokerages
  • By currency: USD, EUR, etc.

Part 5: Import Transactions

CSV import

Date,Type,Symbol,Quantity,Price,Fee,Currency,Account
2025-01-15,BUY,VTI,50,245.30,0,USD,Vanguard
2025-02-01,BUY,AAPL,10,180.50,4.95,USD,Fidelity
2025-03-01,DIVIDEND,VTI,,125.00,0,USD,Vanguard
2025-06-15,SELL,AAPL,5,195.20,4.95,USD,Fidelity

Portfolio → Import → CSV → upload file → map columns → import.

Manual entry tips

  • Enter transactions in chronological order
  • Include dividends for accurate total return
  • Record fees for true cost basis
  • Use the correct currency for each transaction

Part 6: Benchmarking

Compare your portfolio performance against indices:

  1. Settings → Benchmarks
  2. Add: SPY (S&P 500), VT (Total World), custom index
  3. Dashboard → toggle benchmark overlay on performance chart

Example insights

Your portfolio:     +12.3% YTD
S&P 500 (SPY):     +15.1% YTD
Difference:         -2.8% (underperforming)

Part 7: Dividend Tracking

Log dividends

  1. + Add Activity → Dividend
  2. Symbol: VTI
  3. Amount: total dividend received
  4. Date: ex-dividend date
  5. Account: receiving account

Dividend dashboard

Shows:

  • Dividend income by month/year: bar chart of passive income
  • Dividend yield: weighted yield across portfolio
  • Top dividend payers: which holdings contribute most

Part 8: Multi-User and Security

Additional users

Admin → User Management → Invite:
  Email: partner@example.com
  Role: User

Each user has their own private portfolio — no data shared between users.

Security practices

  • No brokerage linking: Ghostfolio never has access to your actual accounts
  • Self-hosted: Data stays on your server
  • Manual entry: You control exactly what data is stored
  • Encryption: Use HTTPS and encrypted database backups

Maintenance

# Update:
docker compose pull
docker compose up -d

# Backup database:
docker exec ghostfolio-db-1 pg_dump -U ghostfolio ghostfolio \
  | gzip > ghostfolio-db-$(date +%Y%m%d).sql.gz

# Logs:
docker compose logs -f ghostfolio

Why Self-Host Ghostfolio

Ghostfolio's premium SaaS tier starts at $16.99/month — $203.88/year — for unlimited portfolio entries, advanced analytics, and priority support. The basic free tier limits portfolios to a single account and restricts some reporting features. Self-hosting gives you the full feature set with no account limits, no usage caps, and no subscription fee. Your only cost is the VPS.

The privacy argument is the stronger motivation for most users. Tracking investments requires entering sensitive financial information: which assets you own, how many shares, when you bought them, and at what price. This is information you wouldn't want to leak to a data breach or share with a SaaS provider's analytics pipeline. Ghostfolio is explicit that it does not require brokerage account linking — everything is entered manually or via CSV. When self-hosted, that data never leaves your server. For investors with significant holdings, this privacy guarantee is worth more than any subscription cost savings.

Ghostfolio's approach of manual entry (rather than automatic brokerage sync like Personal Capital or Wealthfront) is a feature, not a limitation. Automatic sync services require OAuth access to your brokerage accounts — meaning the service can read all your account activity. Ghostfolio's manual model means zero read access to your actual accounts. Yes, it takes a few minutes to enter each transaction, but that manual step is the security boundary that keeps your broker credentials entirely separate from your portfolio tracker.

Multi-currency support is a meaningful practical advantage for international investors. Ghostfolio handles assets denominated in different currencies and converts portfolio value using daily exchange rates. If you hold US stocks, European ETFs, and cryptocurrency, Ghostfolio shows your total net worth in your home currency with appropriate conversion applied historically.

When NOT to self-host Ghostfolio. If you want automatic portfolio sync from your broker, Ghostfolio isn't the tool — and neither is any self-hosted alternative (for obvious security reasons). If you need tax optimization advice, automated rebalancing recommendations, or a robo-advisor, purpose-built SaaS tools serve those use cases better. Ghostfolio is a tracking and visualization tool, not a financial advisor.

Prerequisites

Ghostfolio requires three services: the TypeScript/Angular application, PostgreSQL, and Redis (for caching market data). Together they use around 300–500MB RAM at idle. A Hetzner CX22 (2 vCPU, 4GB RAM) at €4.50/month is the recommended starting point. Market data fetching from Yahoo Finance is periodic (not continuous), so CPU requirements are modest. See the VPS comparison guide for full provider comparisons.

Docker Engine 24+ and Docker Compose v2 are required. Three environment variables must be generated before first run:

echo "DB_PASSWORD=$(openssl rand -base64 24)" >> .env
echo "ACCESS_TOKEN_SALT=$(openssl rand -hex 32)" >> .env
echo "JWT_SECRET=$(openssl rand -hex 32)" >> .env

These values are used to hash access tokens and sign JWTs — if they change after accounts are created, all existing sessions are invalidated and passwords must be reset.

DNS: create an A record for portfolio.yourdomain.com pointing to your server. Caddy handles HTTPS certificate provisioning automatically.

Data providers: Yahoo Finance works out of the box with no API key and covers most US stocks, ETFs, and major international securities. For broader coverage or higher request limits, configure Alpha Vantage (free tier: 25 requests/day) or Coingecko for cryptocurrency prices.

Production Security Hardening

Your investment portfolio data is sensitive financial information. Treating the Ghostfolio server with the same care as any financial account is appropriate.

UFW firewall. Allow only SSH, HTTP, and HTTPS:

ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

Ghostfolio's internal port 3333, PostgreSQL's 5432, and Redis's 6379 should never be publicly exposed.

Fail2ban. Protect SSH and the application login endpoint:

apt install fail2ban -y

Create /etc/fail2ban/jail.local:

[DEFAULT]
bantime  = 2h
findtime = 10m
maxretry = 5

[sshd]
enabled = true
systemctl restart fail2ban

Secrets management. Store all three generated secrets (DB_PASSWORD, ACCESS_TOKEN_SALT, JWT_SECRET) in .env with restricted permissions:

chmod 600 .env

SSH hardening:

# /etc/ssh/sshd_config
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
systemctl restart sshd

Automatic OS updates:

apt install unattended-upgrades -y
dpkg-reconfigure --priority=low unattended-upgrades

Database backups. All portfolio transactions live in PostgreSQL. A data loss event means losing your entire investment history. Schedule daily encrypted backups — the automated backup guide covers Restic-based backup workflows with rotation policies. Use Ghostfolio's built-in CSV export as a secondary backup: Portfolio → Export → CSV, and store the export offline.

For a complete hardening checklist, see the self-hosting security checklist.

Troubleshooting Common Issues

"JWT secret not set" or application fails to start. All three environment variables (ACCESS_TOKEN_SALT, JWT_SECRET, DB_PASSWORD) are required. If any are missing from .env, Ghostfolio refuses to start. Verify your .env file is in the same directory as docker-compose.yml and that Docker Compose is loading it. Check with docker compose config to see resolved environment variables.

Asset price not updating (stale quotes). Ghostfolio fetches market data periodically via Redis-cached jobs. If prices are stale, check whether the Redis container is running: docker compose ps. Also verify your data provider is reachable: docker compose logs ghostfolio | grep "Yahoo". Yahoo Finance occasionally rate-limits requests — if you're seeing consistent failures, configure Alpha Vantage as a secondary provider.

Cannot find asset when adding a holding. The asset search uses the configured data provider's symbol lookup. Try the exact ticker symbol (e.g., AAPL not Apple) or ISIN number. For international assets, the symbol format may differ by exchange — German stocks on XETRA use .DE suffix (e.g., SAP.DE). If an asset isn't in Yahoo Finance's database, you can add it manually as a custom security.

"Database connection failed" on startup. This usually means PostgreSQL hasn't finished initializing before Ghostfolio tries to connect. The compose file's depends_on uses a health check, but on very slow VPSes this may not be enough. Add a restart: unless-stopped to the Ghostfolio service so it retries automatically. Alternatively, start the database first: docker compose up -d db redis, wait 30 seconds, then docker compose up -d ghostfolio.

Portfolio performance calculation is incorrect. Ghostfolio uses time-weighted return (TWR) for performance, which handles cash flows correctly for buy/sell events. If your portfolio shows unexpected returns, verify all transactions are entered in chronological order and that the transaction type (Buy/Sell/Dividend) is correct. A "Sell" entered as a "Buy" will significantly distort performance calculations.

Backup restore fails. Stop Ghostfolio and Redis before restoring the PostgreSQL database: docker compose stop ghostfolio redis. Restore the dump: gunzip -c backup.sql.gz | docker exec -i ghostfolio-db-1 psql -U ghostfolio -d ghostfolio. Then restart: docker compose start redis ghostfolio. Redis cache will be cold after restart and will warm up as market data fetches run.

Portfolio shows incorrect allocation percentages. If your allocation chart doesn't add up to 100%, check whether you have cash accounts that are not assigned an asset class. Ghostfolio needs every account and activity to have a currency assigned. Accounts with no position value (empty accounts) can sometimes cause display rounding issues. Also verify that all your holdings have current market prices — assets with stale or missing prices (often happens with less-traded securities or manual custom assets) may show as 0% allocation.

Ghostfolio container restarting repeatedly. Check logs with docker compose logs ghostfolio. A common cause is the ACCESS_TOKEN_SALT or JWT_SECRET being empty strings rather than genuinely missing — Docker Compose substitutes empty strings for undefined variables. Ensure both values are non-empty 32+ character hex strings in your .env file. Also confirm that the Redis container is healthy before Ghostfolio starts, as the application requires Redis on startup for cache operations.

Dividend payments not showing in the monthly income chart. Dividend activities must be logged with the exact security symbol matching your portfolio entry. If you own VTI and log a dividend under a slightly different symbol (e.g., VTI.US vs VTI), Ghostfolio won't link the dividend to the holding. Check the exact symbol format by looking at how the holding appears in your portfolio and use the same format for dividend entries.

Market data for a custom asset not updating. Custom securities (manually created, not sourced from Yahoo Finance) require manually entering price updates. Ghostfolio doesn't auto-fetch prices for custom assets since there's no external data source to query. Set up a periodic reminder to update custom asset prices, or use the Ghostfolio API to automate price updates from whatever data source you have access to.

See also: Actual Budget — for budgeting and expense tracking

See all open source finance tools at OSSAlt.com/categories/finance.

The SaaS-to-Self-Hosted Migration Guide (Free PDF)

Step-by-step: infrastructure setup, data migration, backups, and security for 15+ common SaaS replacements. Used by 300+ developers.

Join 300+ self-hosters. Unsubscribe in one click.