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/memosinside the container. - The host path
~/.memoswill 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 processorslinux/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, GID10001, usernamenonroot - 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:stableEnvironment variables
All MEMOS_* variables correspond directly to command-line flags. Common ones:
| Variable | Default | Purpose |
|---|---|---|
MEMOS_PORT | 8081 | HTTP listen port (image default: 5230) |
MEMOS_ADDR | `` | Bind address (empty = all interfaces) |
MEMOS_DATA | /var/opt/memos | Data directory |
MEMOS_DRIVER | sqlite | Database backend |
MEMOS_DSN | auto | Database connection string |
MEMOS_INSTANCE_URL | `` | Public instance URL |
MEMOS_DEMO | false | Demo mode |
MEMOS_ALLOW_PRIVATE_WEBHOOKS | false | Allow webhook URLs that resolve to private or reserved IP ranges |
MEMOS_LOG_LEVEL | info | Log 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:stableExternal databases
SQLite is the default and works well for a single-node deployment. If you need MySQL or PostgreSQL, provide:
MEMOS_DRIVER=mysqlorMEMOS_DRIVER=postgresMEMOS_DSNwith 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:stablePostgreSQL 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:stableWhen 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:stableThis works for any MEMOS_* variable.
Container management
docker logs -f memos
docker stop memos
docker start memos
docker restart memosUpgrades
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:stableBecause 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.