Self-Hosting Papra with Docker Compose on a NAS
Papra is an open-source document management system. The Docker Compose install is four steps. The fifth step, fixing database ownership, is the one that isn't documented and will silently break your setup if you skip it.
Directory Setup
SSH into your NAS and create the working directories:
mkdir -p /volume1/docker/papra/app-data/db
mkdir -p /volume1/documents
The db directory holds the SQLite database. Documents live separately at /volume1/documents so they survive container recreation.
Compose File
Generate an auth secret first:
openssl rand -hex 32
Then write the compose file:
cat > /volume1/docker/papra/docker-compose.yml << 'EOF'
services:
papra:
container_name: papra
image: ghcr.io/papra-hq/papra:latest
restart: unless-stopped
ports:
- "1221:1221"
environment:
- AUTH_SECRET=<your-generated-secret>
- APP_BASE_URL=http://<your-nas-hostname>:1221
volumes:
- ./app-data/db:/app/app-data/db
- /volume1/documents:/app/app-data/documents
user: "1000:1000"
EOF
Replace <your-generated-secret> and <your-nas-hostname>. Run id -u && id -g to confirm your actual UID and GID before setting the user field — Synology and UGREEN differ.
APP_BASE_URL must match the URL you use in the browser exactly. A mismatch causes an "Invalid application origin" error on the register page.
Start
cd /volume1/docker/papra && docker compose up -d
Papra runs all database migrations automatically on first start. You'll see 18 migration entries in the logs, then Server started on port 1221.
Fix Database Permissions
Caution
Registration fails silently if the SQLite database is owned by root. The error in the logs is SQLITE_READONLY. The error on screen is "Failed to create user" with no explanation.
The first container run may write db.sqlite as root if your UID/GID setup wasn't right. Fix it with a temporary Alpine container — no sudo required:
docker run --rm \
-v /volume1/docker/papra/app-data:/data \
alpine chown -R 1000:1000 /data
Then restart:
cd /volume1/docker/papra && docker compose restart
Verify before restarting by checking the file owner:
ls -la /volume1/docker/papra/app-data/db/
The db.sqlite file should be owned by your user, not root.
Register
Go to http://<your-nas-hostname>:1221/register. There are no default credentials. The first account you create is your account.
Upgrading
Papra uses image tags rather than pinned versions, so pulling latest is the upgrade path:
cd /volume1/docker/papra
docker compose pull
docker compose up -d
Compose stops the old container, starts the new one, and migrations run automatically. No manual schema steps. Confirm the new image is running:
docker inspect papra --format '{{.Config.Image}}'
docker logs papra 2>&1 | tail -20
The AUTH_SECRET must stay consistent across upgrades — changing it invalidates all existing sessions.