Skip to content

How to Deploy Memos 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 Memos on the smallest VPS you can buy — one container, one SQLite file, a private note stream behind HTTPS, and a backup that takes one command.

Before you start
  • Any small VPS — 1 vCPU / 512 MB RAM is plenty
  • 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 Memos is

Memos is a lightweight note stream: short Markdown notes, links and work-log entries, organised by a timeline, tags and search rather than folders. It feels closer to a private microblog than to a notebook app, which is exactly why people like it for quick capture — you open it, type, and the note lands at the top of the stream.

It is a Go server with a React front end, under the MIT licence, and it runs as a single container backed by SQLite by default. MySQL and PostgreSQL are supported if you already run one, but for one person or a small team SQLite is the right answer: no extra service, one file to back up.

If you want long-form, hierarchical notes instead, look at Trilium; for a team wiki, BookStack.

Server sizing

Memos is one of the smallest things you can self-host. The catalog lists 256 MB of RAM as the minimum, and on our test box — a GCP e2-standard-2 with Ubuntu 26.04 and Docker 29.8.1 — the container idled at about 16 MB of RAM, with about 100 MB of disk for the image and data.

So the constraint is not Memos; it's the reverse proxy and the OS next to it. Any entry-level VPS works, and Memos happily shares a box with other small services. Disk grows mainly with uploaded attachments.

Prepare the server

This guide assumes Docker Engine and the Compose plugin are installed, with 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
sudo ufw status verbose
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)
Vultralso works on
1 vCPU · 1 GB RAM · 25 GB SSD · $5.00/mo
Get Vultr (opens in new tab)

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

Install Memos (Docker Compose)

Upstream publishes neosmemo/memos with a few tag styles: stable is the recommended production default, versioned tags such as 0.31.0 pin exactly, and latest is development-oriented. The container stores everything under /var/opt/memos, runs as a non-root user (UID 10001), and its entrypoint fixes ownership of the mounted folder for you.

mkdir -p ~/memos/data && cd ~/memos
cat > docker-compose.yml <<'YAML'
services:
  memos:
    image: neosmemo/memos:stable
    container_name: memos
    restart: unless-stopped
    ports:
      # Loopback only: Caddy is the only way in from outside.
      - "127.0.0.1:5230:5230"
    volumes:
      - ./data:/var/opt/memos
    environment:
      MEMOS_DRIVER: sqlite
YAML
docker compose up -d

Check that it answers:

for i in $(seq 1 30); do
  code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:5230/)
  [ "$code" = "200" ] && echo "Memos is up" && break
  sleep 2
done
[ "$code" = "200" ]

A note on MEMOS_INSTANCE_URL

Memos has a MEMOS_INSTANCE_URL variable for its canonical external URL. Per the upstream docs, it has a side effect on a fresh instance: leaving it out initialises the instance with private access, while setting it initialises public access (only when no access setting exists yet). This guide leaves it out, so a new instance starts private. You can change the access policy later in the instance settings; changing the URL of an existing instance does not change the policy.

HTTPS + domain

Point an A record such as memos.example.com at the server and terminate TLS in front of 127.0.0.1:5230, following Automatic HTTPS with Caddy:

memos.example.com {
    reverse_proxy 127.0.0.1:5230
}

If Caddy runs as a container, 127.0.0.1 is its own loopback; put it in the same Compose project and use reverse_proxy memos:5230. Upstream also has a reverse proxy page in its deployment docs.

First account and registration

Open https://memos.example.com and create an account: the first account you register becomes the admin. Do that straight after the proxy goes live, before anyone else can reach the page.

Then look at the instance settings as admin. Under general access controls you can turn off user registration, restrict password sign-in, and set the instance's access policy (public or private). For a personal note stream, turn registration off; add people later from the admin settings if you need to.

Getting notes in

The web UI is only one way in. Upstream documents several integrations, all optional:

  • API access — personal access tokens let scripts and other tools create and read memos over HTTP.
  • Webhooks — Memos can call a URL when memos change, which is how you push notes into other systems.
  • Telegram bot — capture notes by messaging a bot, handy on a phone.
  • MCP — an integration page covers connecting Memos to AI assistants through the Model Context Protocol.

Each is configured from the settings once you're logged in, and none of them needs changes to the container.

Securing it

  • Register the admin account first and close registration afterwards.
  • Keep port 5230 on loopback. ss -ltnp | grep 5230 should show 127.0.0.1:5230.
  • Check the visibility of what you post. Memos lets individual notes be private or shared; get into the habit of checking before you post something sensitive on an instance with public access.
  • Mind webhooks. Upstream blocks webhooks to private network addresses by default; only allow specific hosts with MEMOS_WEBHOOK_PRIVATE_NETWORK_ALLOWLIST if you need one.

Backups

With the default SQLite driver, everything is in ./data: the database (memos_prod.db) and an assets/ folder for uploaded files kept on disk. Upstream's safest offline method is to stop Memos and archive the directory, which takes seconds for an app this size:

cd ~/memos
docker compose stop
sudo tar czf memos-backup-$(date +%F).tar.gz -C ~/memos data docker-compose.yml
docker compose start
sudo tar tzf memos-backup-$(date +%F).tar.gz | head

sudo is there because the container's user owns the files in ./data. For an online backup without stopping, upstream documents SQLite's own .backup command against memos_prod.db, run from a machine with the sqlite3 tool. Either way, copy the archive off the server and schedule it with cron. Upstream's advice is worth repeating: test a restore once before you depend on it, and note where your attachments are stored (database, local disk or S3) alongside the backup.

Upgrades

cd ~/memos
docker compose pull
docker compose up -d

stable moves to each new release; data survives because it lives in ./data. Back up first and read the release notes — upstream publishes upgrade notes for specific versions (for example the move to 0.30). If you want to choose exactly when upgrades happen, pin a versioned tag instead of stable, and roll back by restoring the backup and the previous tag.

Troubleshooting

The page is blank or the container restarts. Read docker compose logs memos. A permissions error on /var/opt/memos usually means the folder was changed by hand; the entrypoint fixes ownership on start, and MEMOS_UID / MEMOS_GID override the user if your host needs a different one.

Anyone can see my notes. Check the instance access policy and each note's visibility. Setting MEMOS_INSTANCE_URL on a fresh install initialises public access.

Links in notifications or shared notes point at the wrong address. Set MEMOS_INSTANCE_URL to your https:// URL (with the access-policy caveat above).

Verification + next steps

You're done when you can load https://memos.example.com over a valid certificate, sign in as the admin you created, post a note and find it by tag or search, confirm registration is closed from a private window, and have a backup archive off the box.

From there, try the API, webhooks or the Telegram bot integration, all covered in upstream's integration docs. For hosts, see Best VPS for Self-Hosting.

Next steps

How to self-host Memos →More self-hosted notes tools →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 Ghostfolio 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 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.