Memos
Deploy

Docker

Run Memos in a single Docker container with persistent data.

Docker is the simplest production-style deployment for a single Memos instance.

Quick start

docker run -d \
  --name memos \
  --restart unless-stopped \
  -p 5230:5230 \
  -v ~/.memos:/var/opt/memos \
  neosmemo/memos:stable
  • The container listens on port 5230.
  • Memos stores persistent data in /var/opt/memos inside the container.
  • The host path ~/.memos will contain the SQLite database and local assets.

Image tags

Common tag choices:

  • stable: safest default for production
  • versioned tags such as 0.29.1: fully pinned deployment
  • branch-like tags such as 0.29: latest patch in that line
  • latest: development-oriented and less suitable for production

Platform support

The image ships multi-arch manifests and automatically selects the right variant:

  • linux/amd64 — x86_64 processors
  • linux/arm64 — ARM 64-bit (Apple Silicon, Raspberry Pi 4+)
  • linux/arm/v7 — ARM 32-bit (Raspberry Pi 2/3)

Container user

Memos runs as a non-root user inside the container:

  • UID 10001, GID 10001, username nonroot
  • The entrypoint script automatically fixes ownership of mounted volumes

If your host volume requires a different UID or GID, override them:

docker run -d \
  --name memos \
  -p 5230:5230 \
  -v ~/.memos:/var/opt/memos \
  -e MEMOS_UID=1000 \
  -e MEMOS_GID=1000 \
  neosmemo/memos:stable

Environment variables

All MEMOS_* variables correspond directly to command-line flags. Common ones:

VariableDefaultPurpose
MEMOS_PORT8081HTTP listen port (image default: 5230)
MEMOS_ADDR``Bind address (empty = all interfaces)
MEMOS_DATA/var/opt/memosData directory
MEMOS_DRIVERsqliteDatabase backend
MEMOS_DSNautoDatabase connection string
MEMOS_INSTANCE_URL``Public instance URL
MEMOS_DEMOfalseDemo mode
MEMOS_ALLOW_PRIVATE_WEBHOOKSfalseAllow webhook URLs that resolve to private or reserved IP ranges
MEMOS_LOG_LEVELinfoLog verbosity (debug, info, warn, error)

Typical example:

docker run -d \
  --name memos \
  --restart unless-stopped \
  -p 5230:5230 \
  -v ~/.memos:/var/opt/memos \
  -e MEMOS_PORT=5230 \
  -e MEMOS_DRIVER=sqlite \
  -e MEMOS_INSTANCE_URL=https://memos.example.com \
  neosmemo/memos:stable

External databases

SQLite is the default and works well for a single-node deployment. If you need MySQL or PostgreSQL, provide:

  • MEMOS_DRIVER=mysql or MEMOS_DRIVER=postgres
  • MEMOS_DSN with the appropriate connection string

MySQL example:

docker run -d \
  --name memos \
  -p 5230:5230 \
  -v ~/.memos:/var/opt/memos \
  -e MEMOS_DRIVER=mysql \
  -e MEMOS_DSN="user:password@tcp(mysql-host:3306)/memos" \
  neosmemo/memos:stable

PostgreSQL example:

docker run -d \
  --name memos \
  -p 5230:5230 \
  -v ~/.memos:/var/opt/memos \
  -e MEMOS_DRIVER=postgres \
  -e MEMOS_DSN="postgres://user:password@postgres-host:5432/memos?sslmode=disable" \
  neosmemo/memos:stable

When using an external database, the mounted volume is still relevant for local assets and instance data.

Secrets with _FILE suffix

The entrypoint script supports *_FILE variable suffixes so you can pass credentials via Docker secrets instead of plain environment variables:

echo "postgres://user:password@host:5432/memos" | docker secret create memos_dsn -

docker run -d \
  --name memos \
  -p 5230:5230 \
  -v ~/.memos:/var/opt/memos \
  -e MEMOS_DRIVER=postgres \
  -e MEMOS_DSN_FILE=/run/secrets/memos_dsn \
  --secret memos_dsn \
  neosmemo/memos:stable

This works for any MEMOS_* variable.

Container management

docker logs -f memos
docker stop memos
docker start memos
docker restart memos

Upgrades

docker pull neosmemo/memos:stable
docker stop memos
docker rm memos
docker run -d \
  --name memos \
  --restart unless-stopped \
  -p 5230:5230 \
  -v ~/.memos:/var/opt/memos \
  neosmemo/memos:stable

Because data is stored in the mounted volume, replacing the container is usually safe as long as you keep the same data directory and have backups.

On this page