# ZhFahim/anchor: self-hosted notes with offline mobile editing

> Anchor is a self-hosted TypeScript note application that keeps edits in a local database, then synchronizes them across devices when connectivity returns. Its Docker deployment combines the web interface, API, and embedded Postgres, but the documentation leaves sync conflict handling and backup recovery unspecified.

**ZhFahim/anchor** — Offline first, self hostable note taking application

- Repository: https://github.com/ZhFahim/anchor
- Stars: 1,386 · Forks: 61
- Language: TypeScript
- License: AGPL-3.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/zhfahim-anchor

## Anchor's offline note model and intended users

Anchor addresses a specific tension in self-hosted productivity software. Its server can live on infrastructure controlled by the operator, while notes remain editable on a phone or browser without a network connection. The README states that notes are stored locally, can be changed offline, and synchronize across devices when online. This makes Anchor relevant to people running a private notes service, private families sharing a deployment, or developers who want an application built around a local-first client model rather than a network-required editor.

The application is not limited to plain notepad behavior. Its feature list includes a rich text editor with headings, lists, checkboxes, emphasis, and underlining. Organization relies on tags and colors, while pinning, archiving, local title and content search, and soft-delete trash provide several different paths into the library. Notes can also be shared with another user as either a viewer or editor. The note history feature goes beyond soft deletion because it allows an earlier version to be read and restored.

Attachments support images and audio, reminders can run once or repeat and produce phone notifications, and an Android home screen widget provides a direct route into notes. Import and export cover both full-library backup and movement through Markdown. These details put Anchor closer to a personal information system than a minimal Markdown editor. They also mean that an adopter is choosing a broad application surface, not merely an offline text buffer.

Anchor is less compelling for someone who only wants a hosted SaaS account with no server administration. The repository offers a Docker deployment and an admin panel, rather than a documented hosted endpoint or paid service. Operators must account for persistence, authentication, public URL configuration, and database access even if the first visible workflow resembles an ordinary notes app.

## How Anchor's web, server, mobile, and Postgres layers connect

Based on the repository layout, Anchor separates its TypeScript web application under `web/`, server code under `server/`, and mobile code under `mobile/`. The `Dockerfile` builds the server first, generates its Prisma client, and compiles Nest. It then builds the web package with Next.js while setting `SERVER_URL=http://127.0.0.1:3001`. The file describes the runtime as a single container containing Postgres, the API, and the web application.

That packaging choice is concrete. The final image starts from `postgres:18-alpine`, copies in the Node runtime, adds `curl`, `supervisor`, and required libraries, and sets `PGDATA=/data/postgres`. The visible architecture therefore does not require a separate database container for the default deployment. The Compose file maps the service's port `3000` to host port `3000` and mounts the named volume `anchor_data` at `/data`. In this configuration, the named volume contains both the database area and other persisted application data under the container's configured data root.

The client-side offline behavior is described by the README but not detailed at the protocol level. It identifies a local database as the place where edits work offline and automatic sync as the mechanism that transfers changes when online. No documentation excerpt defines record identifiers, operation logs, conflict resolution, retry policy, or whether concurrent edits merge by field, note, or last write. Those omissions matter. Local-first storage prevents an offline edit from depending on an immediate server response, but it does not by itself explain which result wins when two connected devices edit the same note.

The mobile application is distributed separately through GitHub Releases. The README identifies a universal APK as the recommended choice and also lists smaller architecture-specific APKs. The web application is served by the same published container, while local notification delivery is presented as a mobile capability. The documentation does not explain whether background synchronization is enabled by the operating system or how the app handles a terminated process.

## Deploying Anchor with Docker for a first browser visit

The documented starting point for most users is the pre-built image. Create `docker-compose.yml` with the service, image, volume, and port settings shown by the README. This keeps the database in the named volume rather than in an anonymous container layer.

```yaml
services:
  anchor:
    image: ghcr.io/zhfahim/anchor:latest
    container_name: anchor
    restart: unless-stopped
    ports:
      - "3000:3000"
    volumes:
      - anchor_data:/data

volumes:
  anchor_data:
```

Start the deployment with Compose. The container is configured to restart unless stopped, so it should return after a normal container or host restart.

```bash
docker compose up -d
```

Open `http://localhost:3000` in a browser. The README says defaults work without an environment block, which makes this the shortest documented route to the application. A useful first check is to create a note, disconnect the network, edit it, restore the connection, and open the same account on another client. That procedure follows the stated offline and automatic-sync behavior, although the README does not state exactly when the sync indicator changes.

For a source build, the README instead clones the repository and starts its checked-in Compose configuration. The repository `docker-compose.yml` sets `build`, so Compose builds from the local source rather than pulling the pre-built image.

```bash
git clone https://github.com/zhfahim/anchor.git
cd anchor
docker compose up -d
```

The Android client is obtained from GitHub Releases. The README lists `anchor-{version}.apk` as the universal file recommended for most devices, plus architecture-specific APKs. It does not document a verified release fingerprint or a mobile deployment workflow, so users should not treat the existence of an APK as an explanation of its signing and update policy.

## Authentication and database choices in Anchor

Anchor exposes several environment variables because a local trial and a public deployment have different requirements. `APP_URL` defaults to `http://localhost:3000` and should represent the actual base URL when Anchor is reached through a domain. `JWT_SECRET` is generated automatically when omitted, while a manually configured value must contain at least 16 characters. `CORS_ORIGINS` defaults to allowing all origins, but it can contain a comma-separated allowlist.

Database configuration also has two paths. Leaving `PG_HOST` empty selects embedded Postgres. Setting it allows an external Postgres host, with documented defaults including port `5432`, user `anchor`, password `password`, and database `anchor`. The default password in both the example environment and Compose file should be treated as a configuration starting point rather than a suitable public deployment value. Changing it affects the connection to the selected database.

OIDC support is available for providers including Pocket ID, Authelia, Authentik, and Keycloak. Enabling it requires `OIDC_ISSUER_URL` and `OIDC_CLIENT_ID`. `OIDC_CLIENT_SECRET` is optional because the application supports public clients using PKCE. `DISABLE_INTERNAL_AUTH=true` can hide local login when OIDC is enabled, creating an OIDC-only mode. `USER_SIGNUP` can be `disabled`, `enabled`, or `review`; if it is not set, administrators can control signup through the panel.

These options give an operator control over the visible authentication boundary, but the documentation excerpt does not describe proxy-header handling, TLS termination, session revocation, or OIDC logout propagation. Those are deployment-specific checks, not claims about undocumented Anchor behavior.

## Anchor history, sync conflicts, and recovery limits

Anchor offers note history with restoration and a trash feature with a recovery period. It also describes full-library export and Markdown import. Those are distinct data-protection mechanisms. Restoring an earlier note version addresses an editing mistake. Trash addresses soft deletion within the application. Markdown movement addresses portability for selected notes or library backup. None should be assumed to replace an independently managed server backup.

The repository layout contains Docker, database, mobile, and web directories, but the documentation does not define a backup command for the `anchor_data` volume. It also does not state whether a database backup can be taken while the container is running, how uploaded images and audio are included in a library backup, or how an administrator restores a complete library onto another host. This is a genuine adoption constraint for notes that users cannot afford to lose.

Sync is another boundary. The README promises automatic synchronization, but it does not explain conflict behavior for simultaneous edits, deleted-versus-edited notes, attachment synchronization, or retry after a prolonged offline period. Local search by title and content is documented, which is useful while disconnected, but the full-text implementation is not identified. A deployment that must guarantee deterministic multi-device edits needs these cases resolved before real data is introduced.

The `next` image is a separate risk boundary. According to the README, it is built from the `next` branch and may contain incomplete features or breaking changes. The documentation explicitly says not to use it in production and to back up data before switching. The stable `latest` image avoids that stated beta warning, although the use of a floating tag also means Compose will not pin deployments to an immutable image digest.

## Anchor maintenance, licensing, and upgrade costs

The repository is not archived, and its last push was on 2026-10-09. Recent listed releases include `v0.17.2` on 2026-09-28, `v0.17.1` and `v0.17.0` on 2026-09-04. This supports recent development activity, but it does not establish a long-term support period or a migration guarantee between versions. Upgrade planning must account for application data, the Compose file, environment changes, and any required database migration.

Anchor uses AGPL-3.0. For a company modifying Anchor and offering the modified application over a network, that license's source-code obligations deserve review. This is not legal advice. The relevant project-specific starting points are the `LICENSE` file and the exact deployment or distribution model, rather than the badge in the README.

A real alternative is to use a conventional browser-only Markdown note system with browser storage or a separate desktop and mobile sync provider. That approach avoids operating the combined Postgres, API, web, and mobile stack, but it does not provide the local database, Android widget, phone reminders, per-note sharing, or history features listed for Anchor. Another alternative is a separately deployed note service with multiple containers and documented backup procedures. It may simplify infrastructure boundaries, but it sacrifices the single-container arrangement Anchor documents.

The operational upgrade cost is therefore not limited to replacing a container image. Anchor's named volume is central to persistence, the build combines several application layers, and OIDC or external Postgres changes alter deployment responsibilities. Pinning a release tag and validating restoration are project-specific steps, but the README does not supply those procedures.

## Conclusion

Anchor fits users who want control over a note service, work without a network connection, and can operate Docker with persistent storage. It is a weaker fit for users who need documented conflict resolution, recovery procedures, or a supported hosted service. Before adoption, verify the exact APK signature for Android, confirm that the `/data` volume fits the deployment's retention policy, and test the intended OIDC and external Postgres configuration if those features are required.

## FAQ

### How do I install ZhFahim/anchor with Docker?

Create a Compose file using `ghcr.io/zhfahim/anchor:latest`, publish port `3000`, and mount `anchor_data` at `/data`. Run `docker compose up -d`, then open `http://localhost:3000`.

### Does ZhFahim/anchor work without an internet connection?

Yes. The README states that notes are stored locally and all edits work offline. Synchronization occurs across devices when the client is online.

### Can ZhFahim/anchor use OpenID Connect authentication?

Yes. OIDC requires `OIDC_ISSUER_URL` and `OIDC_CLIENT_ID` when enabled, while `OIDC_CLIENT_SECRET` can be omitted for a public client using PKCE.

### Where is Anchor's Android app available?

The README directs users to GitHub Releases. It lists a universal `anchor-{version}.apk` recommended for most devices and architecture-specific APKs for smaller downloads.

## Sources

- [Issues](https://github.com/ZhFahim/anchor/issues)
- [License: AGPL-3.0](https://github.com/ZhFahim/anchor/blob/main/LICENSE)
- [README](https://github.com/ZhFahim/anchor/blob/main/README.md)
- [Releases](https://github.com/ZhFahim/anchor/releases)
- [ZhFahim/anchor on GitHub](https://github.com/ZhFahim/anchor)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/zhfahim-anchor
