# Docker
URL: https://usememos.com/docs/deploy/docker

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

## Quick start [#quick-start]

```bash
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 [#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 [#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 [#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:

```bash
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 [#environment-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:

```bash
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 [#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:

```bash
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:

```bash
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 [#secrets-with-_file-suffix]

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

```bash
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 [#container-management]

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

## Upgrades [#upgrades]

```bash
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.
