Explore Technology Practical Guides Mac Smart Home Resume Projects Search the site
Technology

How To Install authentik on a Synology NAS Behind Cloudflare Tunnel

April 3, 2026 Freddy Reyes

Deploy authentik on Synology DSM with Docker Compose, optionally expose it through Cloudflare Tunnel, and avoid stale-volume upgrade issues.

How To Install authentik on a Synology NAS Behind Cloudflare Tunnel

If you want self-hosted identity and SSO on a Synology NAS, authentik is a strong option. This guide walks through a clean Docker Compose deployment on Synology DSM, optional Cloudflare Tunnel exposure, and the one big issue that caused trouble during setup: stale Docker volumes from previous authentik versions.

This post is intentionally sanitized for public sharing. All IP addresses, domains, usernames, passwords, tokens, email addresses, and secret keys below are placeholders.

## What this setup does

- Runs authentik with Docker Compose on Synology DSM

- Uses PostgreSQL and Redis as required services

- Exposes authentik locally on port `9000`

- Optionally publishes the service through Cloudflare Tunnel

- Avoids a broken database state caused by reusing old Docker volumes across authentik version changes

## Requirements

- A Synology NAS with Docker or Container Manager available

- SSH access to the NAS

- A working folder for your compose project, for example:

- `/volume1/docker/authentik`

- Optional:

- A Cloudflare Tunnel already running on the NAS or another host

- A public hostname such as `auth.example.com`

## Recommended directory layout

Create a project folder such as:

```text

/volume1/docker/authentik

```

Inside it, keep at least:

- `compose.yml`

- `.env`

- `data/`

- `certs/`

- `custom-templates/`

## Docker Compose file

Below is a working compose file structure for Synology. Replace image tags and environment values as needed.

```yaml

services:

postgresql:

image: docker.io/library/postgres:16-alpine

container_name: authentik-postgresql

restart: unless-stopped

environment:

POSTGRES_DB: ${PG_DB}

POSTGRES_USER: ${PG_USER}

POSTGRES_PASSWORD: ${PG_PASS}

volumes:

- authentik_database:/var/lib/postgresql/data

redis:

image: docker.io/library/redis:alpine

container_name: authentik-redis

restart: unless-stopped

command: —save 60 1 —loglevel warning

volumes:

- authentik_redis:/data

server:

image: ${AUTHENTIK_IMAGE}:${AUTHENTIK_TAG}

container_name: authentik-server

restart: unless-stopped

command: server

depends_on:

- postgresql

- redis

environment:

AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}

AUTHENTIK_REDIS__HOST: redis

AUTHENTIK_POSTGRESQL__HOST: postgresql

AUTHENTIK_POSTGRESQL__NAME: ${PG_DB}

AUTHENTIK_POSTGRESQL__USER: ${PG_USER}

AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}

AUTHENTIK_ERROR_REPORTING__ENABLED: “false”

AUTHENTIK_EMAIL__FROM: ${AUTHENTIK_EMAIL__FROM}

AUTHENTIK_BOOTSTRAP_PASSWORD: ${AUTHENTIK_BOOTSTRAP_PASSWORD}

AUTHENTIK_BOOTSTRAP_TOKEN: ${AUTHENTIK_BOOTSTRAP_TOKEN}

ports:

- ”${COMPOSE_PORT_HTTP}:9000”

- ”${COMPOSE_PORT_HTTPS}:9443”

volumes:

- ./data:/media

- ./certs:/certs

- ./custom-templates:/templates

worker:

image: ${AUTHENTIK_IMAGE}:${AUTHENTIK_TAG}

container_name: authentik-worker

restart: unless-stopped

command: worker

depends_on:

- postgresql

- redis

environment:

AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}

AUTHENTIK_REDIS__HOST: redis

AUTHENTIK_POSTGRESQL__HOST: postgresql

AUTHENTIK_POSTGRESQL__NAME: ${PG_DB}

AUTHENTIK_POSTGRESQL__USER: ${PG_USER}

AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}

AUTHENTIK_ERROR_REPORTING__ENABLED: “false”

volumes:

- /var/run/docker.sock:/var/run/docker.sock

- ./data:/media

- ./certs:/certs

- ./custom-templates:/templates

volumes:

authentik_database:

authentik_redis:

```

## Example `.env` file

Use strong random values for every secret.

```dotenv

AUTHENTIK_IMAGE=ghcr.io/goauthentik/server

AUTHENTIK_TAG=2025.10.3

PG_DB=authentik

PG_USER=authentik

PG_PASS=replace-with-a-long-random-password

AUTHENTIK_SECRET_KEY=replace-with-a-long-random-secret-key

AUTHENTIK_BOOTSTRAP_PASSWORD=replace-with-a-temporary-bootstrap-password

AUTHENTIK_BOOTSTRAP_TOKEN=replace-with-a-long-random-bootstrap-token

COMPOSE_PORT_HTTP=9000

COMPOSE_PORT_HTTPS=9443

AUTHENTIK_HOST=https://auth.example.com

AUTHENTIK_EMAIL__FROM=authentik@example.com

```

## Start authentik

From the project directory:

```bash

docker compose up -d

```

Then inspect startup logs:

```bash

docker logs —since 2m authentik-server

```

On a clean start, you want migrations to complete and the server to settle without repeated database errors.

## Local access

Test local access first before introducing Cloudflare:

```text

http://NAS-LAN-IP:9000/if/flow/default-authentication-flow/?next=%2F

```

Replace `NAS-LAN-IP` with your own private address.

Testing locally first makes it much easier to separate application problems from reverse proxy or tunnel problems.

## Optional: publish through Cloudflare Tunnel

If you already use `cloudflared`, add an ingress rule for authentik that points to the NAS over HTTP on port `9000`.

Example:

```yaml

ingress:

- hostname: auth.example.com

service: http://NAS-LAN-IP:9000

- service: http_status:404

```

Then restart the tunnel service or container.

At that point, both of these should work:

- Local: `http://NAS-LAN-IP:9000\`

- Public: `https://auth.example.com\`

## The problem we hit

The biggest issue during setup was not Cloudflare, the browser, or the admin password. The real problem was a broken PostgreSQL schema caused by stale Docker volumes from earlier authentik attempts and version switching.

The key error in the logs looked like this:

```text

column authentik_core_group.parent_id does not exist

```

There were also related errors such as:

```text

Internal Server Error: /api/v3/outposts/instances/

found incompatible flow plan, invalidating run

```

Those symptoms caused logins to fail in confusing ways even after:

- resetting the admin password

- generating a recovery key

- verifying the admin user was active

In short: the app looked alive, but the database state was wrong.

## Why this happened

On Synology, deleting and recreating a Docker Compose project does not always remove named Docker volumes. That means a fresh-looking deployment may still be attached to an old PostgreSQL data directory.

If you deploy one authentik version, then switch versions, then reuse the same database volume, you can end up with schema drift or incompatible state.

## How we fixed it

The permanent fix was to remove the stale Docker volumes completely and start from a truly clean database.

First, inspect volumes:

```bash

docker volume ls | grep authentik

```

Stop the stack:

```bash

cd /volume1/docker/authentik

docker compose down

```

Then remove the old authentik-related volumes. Your names may differ, but in this case they included entries like:

```text

authentik_authentik_database

authentik_authentik_pgdata

authentik_authentik_redis

authentik_database

```

Remove the stale ones:

```bash

docker volume rm VOLUME_NAME

```

Repeat until:

```bash

docker volume ls | grep authentik

```

returns nothing relevant from the old deployment.

If you also store bind-mounted application files locally, clear those too if you want a full reset:

```bash

rm -rf /volume1/docker/authentik/data/*

rm -rf /volume1/docker/authentik/certs/*

rm -rf /volume1/docker/authentik/custom-templates/*

```

After that, start the stack again:

```bash

docker compose up -d

docker logs —since 2m authentik-server

```

Once the old volumes were gone, authentik booted correctly and login started working.

## Important lessons learned

- Test authentik locally before troubleshooting Cloudflare Tunnel

- Do not assume deleting a Synology project also deleted Docker volumes

- Do not switch authentik versions back and forth on the same database unless the upgrade path is known-good

- If authentik shows repeated login flow errors, inspect the server logs before resetting passwords repeatedly

- If PostgreSQL schema errors appear, fix the database state first

## Safe troubleshooting checklist

If authentik is up but login is failing:

  1. Check the local URL first

  2. Review `authentik-server` logs

  3. Look for database schema errors

  4. Confirm PostgreSQL and Redis are healthy

  5. Only reset passwords after the database is known-good

  6. If you reused volumes across versions, strongly consider a clean rebuild

## Security notes before publishing your own guide

Before posting screenshots, configs, or logs publicly, remove or replace:

- Private IP addresses

- Public domains

- Email addresses

- Usernames

- Bootstrap tokens

- Session or recovery links

- Secret keys

- Database passwords

- Any Cloudflare Tunnel identifiers

Treat bootstrap tokens, recovery links, and temporary passwords as compromised if they were ever pasted into chat, screenshots, or public notes. Rotate them after the system is stable.

## Final result

After removing the stale volumes and redeploying cleanly, authentik worked correctly on the Synology NAS both locally and through Cloudflare Tunnel.

If you are doing a similar deployment and authentik behaves strangely even though the containers are running, check the database volumes early. That ended up being the difference between a half-working install and a fully working one.