Self-hosted markdown notes with fast search, tags, attachments, and zero database.
CopyCat is a distraction-free note app for people who want plain markdown files, fast full-text search, and a simple self-hosted setup. Notes stay on disk as regular files, metadata lives in a lightweight hidden app directory, and the search index is rebuildable cache rather than a database.
Keywords: self-hosted, markdown, notes, wiki, search, tags, attachments, docker, fastapi, vue
- Plain markdown storage
- Fast full-text search
- Custom tags and favorites
- Attachments
- Raw and WYSIWYG editor modes
- Wikilinks like
[[Another Note]] - Mobile-friendly UI
- Light and dark themes
- Authentication modes: none, read-only, password, TOTP
- API docs exposed at
/docs - Multi-group access model for managed users
docker build -t copycat:latest .docker run -d \
--name copycat \
-e PUID=1000 \
-e PGID=1000 \
-e COPYCAT_AUTH_TYPE=password \
-e COPYCAT_USERNAME=admin \
-e COPYCAT_PASSWORD='changeMe!' \
-e COPYCAT_SECRET_KEY='replace-this-with-a-long-random-secret' \
-v "$(pwd)/data:/data" \
-p 8080:8080 \
ghcr.io/yanadevops/copycat:latestOpen http://localhost:8080.
services:
copycat:
container_name: copycat
build: .
image: ghcr.io/yanadevops/copycat:latest
environment:
PUID: 1000
PGID: 1000
COPYCAT_AUTH_TYPE: password
COPYCAT_USERNAME: admin
COPYCAT_PASSWORD: changeMe!
COPYCAT_SECRET_KEY: replace-this-with-a-long-random-secret
volumes:
- ./data:/data
ports:
- "8080:8080"
restart: unless-stoppedStart it with:
docker compose up --build -dThe repository includes a Helm chart at helm/copycat for Kubernetes deployments.
What the chart creates:
DeploymentorStatefulSetServicePersistentVolumeClaimor StatefulSetvolumeClaimTemplate- headless
ServiceforStatefulSet - auth
Secretor support for an existing Secret - optional
Ingress
Important defaults:
controller.type=Deployment- single replica
Recreatedeployment strategy- persistent data mounted at
/data
These defaults are intentional for a stateful single-volume setup.
helm upgrade --install copycat ./helm/copycat \
--namespace copycat \
--create-namespace \
--set image.repository=your-registry/copycat \
--set image.tag=latest \
--set auth.username=admin \
--set auth.password='changeMe!' \
--set auth.secretKey='replace-this-with-a-long-random-secret'Use this when you want a StatefulSet-managed pod identity. For new installs, the cleanest option is a volumeClaimTemplate.
Keep replicaCount: 1 for the normal single-volume setup.
controller:
type: StatefulSet
persistence:
volumeClaimTemplate:
enabled: true
size: 10GiInstall it with:
helm upgrade --install copycat ./helm/copycat \
--namespace copycat \
--create-namespace \
-f ./helm/copycat/values-statefulset.yaml \
--set image.repository=your-registry/copycat \
--set image.tag=latest \
--set auth.username=admin \
--set auth.password='changeMe!' \
--set auth.secretKey='replace-this-with-a-long-random-secret'persistence:
existingClaim: copycat-dataThis also works with controller.type=StatefulSet if you already have a claim and do not want Helm to create per-pod claims.
The existing Secret should expose these keys when auth.type is password or totp:
COPYCAT_USERNAMECOPYCAT_PASSWORDCOPYCAT_SECRET_KEYCOPYCAT_TOTP_KEYfor TOTP only
Example:
auth:
type: password
existingSecret: copycat-authingress:
enabled: true
className: nginx
hosts:
- host: copycat.example.com
paths:
- path: /
pathType: PrefixIf you publish CopyCat under a subpath, set both the ingress path and pathPrefix:
pathPrefix: /copycat
ingress:
enabled: true
className: nginx
hosts:
- host: example.com
paths:
- path: /copycat
pathType: PrefixIf you already have a persistent /data volume, the app keeps using the same data. Root metadata is migrated into /data/.copycat automatically on startup.
| Variable | Required | Default | Purpose |
|---|---|---|---|
COPYCAT_AUTH_TYPE |
No | password |
Auth mode: none, read_only, password, totp |
COPYCAT_USERNAME |
Password/TOTP | - | Bootstrap admin username |
COPYCAT_PASSWORD |
Password/TOTP | - | Bootstrap admin password |
COPYCAT_SECRET_KEY |
Password/TOTP | - | Session signing secret |
COPYCAT_TOTP_KEY |
TOTP only | - | TOTP seed for admin login |
COPYCAT_PATH |
No | /data |
Data directory inside the container |
COPYCAT_HOST |
No | 0.0.0.0 |
Bind address |
COPYCAT_PORT |
No | 8080 |
HTTP port |
PUID |
No | 1000 |
Runtime user ID for mounted volumes |
PGID |
No | 1000 |
Runtime group ID for mounted volumes |
| Variable | Default | Purpose |
|---|---|---|
COPYCAT_PATH_PREFIX |
empty | Serve the app behind a reverse-proxy subpath |
COPYCAT_QUICK_ACCESS_HIDE |
false |
Hide the quick-access block on the home page |
COPYCAT_QUICK_ACCESS_TITLE |
RECENTLY MODIFIED |
Custom home page block title |
COPYCAT_QUICK_ACCESS_TERM |
* |
Custom search term for the home page block |
COPYCAT_QUICK_ACCESS_SORT |
lastModified |
Sort mode for the home page block |
COPYCAT_QUICK_ACCESS_LIMIT |
4 |
Number of items in the home page block |
COPYCAT_LOGIN_RATE_LIMIT_ENABLED |
true |
Enable login rate limiting |
COPYCAT_LOGIN_RATE_LIMIT_WINDOW_SECONDS |
60 |
Login rate-limit window |
COPYCAT_LOGIN_RATE_LIMIT_IP_MAX |
10 |
Max failed attempts per IP |
COPYCAT_LOGIN_RATE_LIMIT_USER_IP_MAX |
5 |
Max failed attempts per username and IP |
COPYCAT_CSP_MODE |
report-only |
Content Security Policy mode |
COPYCAT_MAX_ATTACHMENT_BYTES |
26214400 |
Max attachment size in bytes |
COPYCAT_ATTACHMENT_BLOCK_ACTIVE_CONTENT |
false |
Block risky attachment types |
COPYCAT_ATTACHMENT_BLOCKED_EXTENSIONS |
safe default list | Override blocked file extensions |
COPYCAT_SET_HTTPONLY_AUTH_COOKIE |
false |
Store auth token in an HTTP-only cookie |
Everything under /data persists across container restarts.
/data/
.copycat/
auth/
groups.json
users.json
metadata.json
index/
attachments/
groups/
<group-slug>/
notes/
attachments/
.copycat/
metadata.json
index/
Notes:
- Bootstrap admin credentials come from environment variables, not from files inside
/data. - Managed users and groups are stored inside
/data/.copycat/auth. - Favorites, tags, and note metadata are stored in
metadata.json. - Search index files are cache and can be rebuilt.
- Older root metadata directories are migrated automatically into
.copycat.
Useful checks inside the container:
cd /data
ls -la
ls -la /data/.copycat
ls -la /data/.copycat/auth
cat /data/.copycat/metadata.json
cat /data/.copycat/auth/groups.json
cat /data/.copycat/auth/users.json
ls -la /data/groups/<group-slug>/.copycat
cat /data/groups/<group-slug>/.copycat/metadata.jsonnpm install
npm run devnpm run buildThe backend runs with FastAPI and Uvicorn through the container entrypoint. For local container-based development:
docker compose up --buildIssues and pull requests are welcome. Read CONTRIBUTING.md before opening a PR.
If CopyCat saves you time, you can support the project here:
This project is released under the MIT License.
- Whoosh for search indexing
- TOAST UI Editor for markdown editing




