Skip to content

How to Deploy Ghostfolio on a VPS

Updated Sep 2026

verified on Ubuntu 26.04 · Sep 2026
We earn commissions when you shop through the links below. Full disclosure →

Self-host Ghostfolio, the open-source wealth tracker, with its official Docker Compose stack (app, PostgreSQL and Redis), then close signups after you create the admin.

Before you start
  • A VPS with 1 vCPU / 2 GB RAM
  • A fresh Ubuntu 24.04 or 26.04 server with root/sudo SSH access
  • A domain you can point at the server
  • Docker Engine + Compose installed (see the base guide below)
Need a box for this guide? Kamatera's free tier lets you spin one up now.Start free on Kamatera → (opens in new tab)

What Ghostfolio is

Ghostfolio is an open-source wealth management app for investors. It tracks stocks, ETFs and cryptocurrencies across brokers and exchanges in one dashboard, and reports portfolio performance, allocations and risk. It is a mobile-first web app you can install to your home screen, and it is licensed AGPL-3.0.

Under the hood it is a TypeScript project: a NestJS backend with PostgreSQL (through Prisma) as the database and Redis as a cache, and an Angular frontend. The official Docker Compose file runs all three as containers.

Two things to know before you install:

  • Ghostfolio tracks investments, not spending. It records buys, sells, dividends, fees and similar activities. For budgets, pair it with Actual Budget or Firefly III. Firefly III vs Ghostfolio explains the split.
  • Market data comes from outside providers. A self-hosted instance works with the free providers Ghostfolio supports. Ghostfolio's own professional data provider needs a Premium subscription's API key.

Server sizing

Upstream publishes no RAM figure. The catalog uses 1 GB as its estimate for the three containers. On our test box (GCP e2-standard-2, Ubuntu 26.04, Docker 29.8.1), the idle stack measured about 465 MB of RAM and about 2 GB of disk for the images and database.

  • 2 GB RAM is the comfortable size. It leaves headroom for PostgreSQL to grow and for a reverse proxy on the same box.
  • Disk: 20 GB is plenty. The database holds your transactions and cached market data, which is small next to the images.

Official images exist for linux/amd64, linux/arm64 and linux/arm/v7, so an ARM VPS works too.

Prepare the server

This guide assumes Docker Engine and the Compose plugin are installed, along with a non-root user and a ufw firewall. If not, start with Docker & Compose on Ubuntu.

sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw --force enable
Where to host itaffiliate disclosure
Hetzner Cloudrun it on
2 vCPU · 4 GB RAM · 80 GB SSD · $23.59/mo
Get Hetzner Cloud (opens in new tab)
Kamaterafree trial
1 vCPU · 1 GB RAM · 20 GB SSD · $4.00/mo
Start free on Kamatera → (opens in new tab)
DigitalOceanalso works on
1 vCPU · 1 GB RAM · 25 GB SSD · $6.00/mo
Deploy on DigitalOcean → (opens in new tab)

Paid link — we earn a commission if you shop through it.

Install Ghostfolio (Docker Compose)

Upstream's route is to clone the repository, copy .env.example to .env, fill it in, and start docker/docker-compose.yml:

cd ~
git clone --depth 1 https://github.com/ghostfolio/ghostfolio.git
cd ~/ghostfolio
cp .env.example .env

The example file has four placeholders: REDIS_PASSWORD, POSTGRES_PASSWORD, ACCESS_TOKEN_SALT and JWT_SECRET_KEY. Generate all four. Two optional settings from the README also belong here once you add a domain:

  • ROOT_URL, the public URL. Ghostfolio uses it to build callback URLs and external links.
  • TRUST_PROXY=1, which tells the Express server that it sits behind one reverse proxy. Rate limiting then sees the real client IP.

Replace folio.example.com with your hostname:

cd ~/ghostfolio
DOMAIN=folio.example.com
sed -i "s|<INSERT_REDIS_PASSWORD>|$(openssl rand -hex 24)|; s|<INSERT_POSTGRES_PASSWORD>|$(openssl rand -hex 24)|; s|^ACCESS_TOKEN_SALT=.*|ACCESS_TOKEN_SALT=$(openssl rand -hex 32)|; s|^JWT_SECRET_KEY=.*|JWT_SECRET_KEY=$(openssl rand -hex 32)|" .env
grep -q '^ROOT_URL=' .env || printf 'ROOT_URL=https://%s\nTRUST_PROXY=1\n' "$DOMAIN" >> .env
grep -c 'INSERT_' .env || true

The last command should print 0, which means no placeholder is left. Generate the Postgres password before the first start. DATABASE_URL is built from it, and PostgreSQL keeps whatever password it was initialised with.

The compose file publishes port 3333 on every interface. Bind it to loopback so the reverse proxy is the only way in:

cd ~/ghostfolio
sed -i 's|- 3333:3333|- 127.0.0.1:3333:3333|' docker/docker-compose.yml
grep -n '3333:3333' docker/docker-compose.yml

Start the stack. The app waits for PostgreSQL and Redis to report healthy, applies database migrations, and then answers on its health endpoint:

cd ~/ghostfolio
docker compose -f docker/docker-compose.yml up -d
timeout 600 bash -c 'until curl -fsS http://127.0.0.1:3333/api/v1/health; do sleep 5; done'
echo
docker compose -f docker/docker-compose.yml ps

HTTPS + domain

Point an A record for folio.example.com at the server, wait for it to resolve, then put Caddy in front of the loopback port. The setup is in Automatic HTTPS with Caddy:

folio.example.com {
    reverse_proxy 127.0.0.1:3333
}

If Caddy runs as a container, 127.0.0.1 is its own loopback. Attach it to the ghostfolio compose network and proxy to ghostfolio:3333 instead.

First login, then close signups

Open https://folio.example.com and choose Get Started. Upstream's setup note says the first user created gets the ADMIN role.

By default, Ghostfolio logs you in with a Security Token rather than a password (ENABLE_FEATURE_AUTH_TOKEN defaults to true). The token is shown when the account is created. Store it in your password manager, because it is your login. You can generate a new one later from the account access panel.

User signup is on by default. Anyone who can reach the URL can create an account on your server. Once your admin account exists, open the Admin Control panel and switch off User Signup. Check the result in a private window: the Get Started flow should no longer create accounts.

For a team, the README documents experimental OpenID Connect login (ENABLE_FEATURE_AUTH_OIDC plus the OIDC_* variables). It pairs with a self-hosted identity provider such as Authentik.

Securing it

  • Keep port 3333 on loopback. The sed step above does this, so traffic reaches the app only through Caddy and its certificate.
  • Never commit or share .env. It holds the database and Redis passwords and the two signing secrets. Rotating JWT_SECRET_KEY logs everyone out.
  • The cap_drop: ALL and no-new-privileges settings in upstream's compose file are deliberate hardening. Leave them in place.

Backups

Everything that matters is in PostgreSQL and in .env. Dump the database with the tools inside the Postgres container, which reads its own credentials from the environment:

cd ~/ghostfolio
mkdir -p ~/backups/ghostfolio
docker compose -f docker/docker-compose.yml exec -T postgres \
  sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' | gzip > ~/backups/ghostfolio/ghostfolio-$(date +%F).sql.gz
cp .env ~/backups/ghostfolio/env-$(date +%F)
ls -lh ~/backups/ghostfolio

Copy that folder off the box. Ghostfolio also has an in-app export of your activities to a JSON file, and the matching import. That makes a useful second, provider-independent copy of your transactions, but it does not include users or settings.

Upgrades

Upstream's procedure for the latest tag is to pull the image and start it again. The container applies any schema migrations on startup:

cd ~/ghostfolio
docker compose -f docker/docker-compose.yml pull
docker compose -f docker/docker-compose.yml up -d

Take a pg_dump first, because migrations are one-way. If you would rather choose when to move, replace latest in docker/docker-compose.yml with a specific version tag and raise it on your own schedule, which is the other route upstream describes.

Troubleshooting

docker compose up fails with "REDIS_PASSWORD variable is not set". The Redis service refuses to start without a password. Run the sed step above and check that grep -c INSERT_ .env prints 0.

The app never becomes healthy. Check docker compose -f docker/docker-compose.yml logs ghostfolio. The usual cause is a DATABASE_URL that no longer matches the password PostgreSQL was initialised with, which happens when .env is edited after the first start. Put the original password back, or recreate the database volume if nothing is in it yet.

Prices or holdings show no market data. Your data provider has no data for that symbol, or the request timed out. REQUEST_TIMEOUT defaults to 2000 ms, which is short on a slow link. The Premium-only provider is not available on a self-hosted instance without its API key.

Strangers are creating accounts. User Signup is still on. Turn it off in the Admin Control panel and delete the accounts you don't recognise.

Verification + next steps

You're done when you can load https://folio.example.com over a valid certificate and log in with your Security Token. Your account should show admin access, a private window should show that signup is closed, and you should have an off-box pg_dump that you have restored at least once.

From there: import your transaction history, add your accounts, and set a benchmark to compare against. If you run budgeting next to it, Deploy Actual Budget on a VPS fits on the same box.

Next steps

How to self-host Ghostfolio →Automatic HTTPS with Caddy →Run Claude Code with Ollama on Your Own VPS →Deploy Coolify on a VPS →How to Deploy Actual Budget on a VPS →How to Deploy AnythingLLM on a VPS →How to Deploy Appwrite on a VPS →How to Deploy Audiobookshelf on a VPS →How to Deploy Authelia on a VPS →How to Deploy authentik on a VPS →How to Deploy Baserow on a VPS →How to Deploy Beszel on a VPS →How to Deploy Bitwarden on a VPS →How to Deploy BookStack on a VPS →How to Deploy CapRover on a VPS →How to Deploy Checkmate on a VPS →How to Deploy Directus on a VPS →How to Deploy docker-mailserver on a VPS →How to Deploy Docmost on a VPS →How to Deploy Dokku on a VPS →How to Deploy Dokploy on a VPS →How to Deploy Firefly III on a VPS →How to Deploy Forgejo on a VPS →How to Deploy Gatus on a VPS →How to Deploy Gitea on a VPS →How to Deploy GitLab on a VPS →How to Deploy GlitchTip on a VPS →How to Deploy Grafana on a VPS →How to Deploy Graylog on a VPS →How to Deploy Headscale on a VPS →How to Deploy Healthchecks on a VPS →How to Deploy Home Assistant on a VPS →How to Deploy Immich on a VPS →How to Deploy Jan on a VPS →How to Deploy Jellyfin on a VPS →How to Deploy Karakeep on a VPS →How to Deploy Keycloak on a VPS →How to Deploy Leantime on a VPS →How to Deploy LibreChat on a VPS →How to Deploy Linkwarden on a VPS →How to Deploy LocalAI on a VPS →How to Deploy Mailcow on a VPS →How to Deploy Mailu on a VPS →How to Deploy Matomo on a VPS →How to Deploy Mattermost on a VPS →How to Deploy Meilisearch on a VPS →How to Deploy Memos on a VPS →How to Deploy n8n on a VPS →How to Deploy Navidrome on a VPS →How to Deploy NetBird on a VPS →How to Deploy Netdata on a VPS →How to Deploy Nextcloud on a VPS →How to Deploy Next.js to a VPS →How to Deploy Nginx Proxy Manager on a VPS →How to Deploy NocoDB on a VPS →How to Deploy ntfy on a VPS →How to Deploy Ollama on a VPS →How to Deploy Open WebUI on a VPS →How to Deploy OpenHands on a VPS →How to Deploy OpenObserve on a VPS →How to Deploy OpenProject on a VPS →How to Deploy Outline on a VPS →How to Deploy Pangolin on a VPS →How to Deploy Paperless-ngx on a VPS →How to Deploy Passbolt on a VPS →How to Deploy Plane on a VPS →How to Deploy Plausible Analytics on a VPS →How to Deploy Pocket ID on a VPS →How to Deploy PocketBase on a VPS →How to Deploy Prometheus on a VPS →How to Deploy Psono on a VPS →How to Deploy Radarr on a VPS →How to Deploy Rocket.Chat on a VPS →How to Deploy SigNoz on a VPS →How to Deploy Sonarr on a VPS →How to Deploy Stalwart on a VPS →How to Deploy Stirling-PDF on a VPS →How to Deploy Supabase on a VPS →How to Deploy Synapse on a VPS →How to Deploy Taiga on a VPS →How to Deploy TeamPass on a VPS →How to Deploy Tinyauth on a VPS →How to Deploy Traefik on a VPS →How to Deploy Trilium on a VPS →How to Deploy Twenty CRM on a VPS →How to Deploy Umami on a VPS →How to Deploy Uptime Kuma on a VPS →How to Deploy Vaultwarden on a VPS →How to Deploy Vikunja on a VPS →How to Deploy wg-easy on a VPS →How to Deploy Wiki.js on a VPS →How to Deploy Zabbix on a VPS →How to Deploy Zitadel on a VPS →How to Deploy Zulip on a VPS →Docker & Compose on Ubuntu 26.04 →Building AI Workflows with n8n →Install Open WebUI with Ollama →Adding AI-Powered Insights to Plausible Analytics →Building AI-Powered Apps with Supabase and pgvector →

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.