selfhost.directory

The index of self-hostable projects — ranked, categorized and served with live updates.
Own your data and stop giving it to big tech.

altero logo

Self-host altero

Self-hosted Zotero sync server with full API compatibility

BetaOpen SourceAGPL-3.0
Updatedlast week
LanguageTypescript
Setup difficultyModerate
Stars
195
RAM
125 MB idle, ~130 MB after light traffic, ~133 MB peak
Port
8000 (HTTP API and web interface)
Docker
ghcr.io/eseifert/altero
Install with Docker Compose

What is altero?

altero runs a synchronization server for Zotero Desktop that implements the Zotero Web API v3. Users point the unmodified desktop client at their own instance by changing two hidden preferences. The server handles personal and group libraries with items, collections, tags, notes, annotations, attachments, and full-text search. It supports SQLite for single users and PostgreSQL for shared deployments. A web interface provides library browsing, account management, group administration, and instance administration. Beyond basic compatibility, altero adds institutional sign-in via OpenID Connect and SAML 2.0, an OAuth 2.0/OpenID Connect authorization server for scoped third-party access, passkeys and second factors, per-member group permissions, shared collection links, group activity notifications, server-side tag rename, configurable retention policies, and migration from zotero.org preserving object keys. Deployment options include Docker Compose with a published image at ghcr.io/eseifert/altero, a source installation with Python 3.14+ and uv, and Podman. The project is licensed under AGPL-3.0-or-later and is currently in beta (1.0.0-beta.3).

Should you self-host altero?

altero scores 47/100 on selfhost.directory (how scoring works) and ranks #8 of 15 in File Sync. It has 195 GitHub stars.

It shipped 2 stable releases in the past year. The latest, v0.9.0, came out on 20 August 2026.

Setup is moderate: expect some configuration beyond a single command. An official Docker image is available. It is AGPL-3.0-licensed and written mostly in Typescript.

Its closest alternative on selfhost.directory is Syncthing (90/100, 89 834 stars). It scores higher overall, so compare the two before you pick. See altero vs Syncthing side by side.

Features

  • Docker Compose Deployment
  • Full library synchronization
  • Group activity and notifications
  • Migration from zotero.org
  • OAuth 2.0 / OpenID Connect authorization server
  • OpenID Connect sign-in
  • Passkeys and second factorsRequires a stable public URL
  • Per-member group permissions
  • Retention and storage reporting
  • SAML 2.0 sign-in
  • Server-side tag rename
  • Shared collection links
  • Web Interface
  • Zotero Web API v3 compatibility

altero README

How to install altero

Install altero with Docker Compose

Docker and Docker Compose
Environment variables
ALTERO_IMAGE_TAG, POSTGRES_PASSWORD, ALTERO_PUBLIC_URL, ALTERO_SMTP_URL, ALTERO_MAIL_FROM, ALTERO_RATE_LIMIT, ALTERO_FORWARDED_ALLOW_IPS, and all ALTERO_* settings

mkdir altero && cd altero curl -fsSLO https://raw.githubusercontent.com/eseifert/altero/v1.0.0-beta.3/docker/compose.yaml ALTERO_IMAGE_TAG=1.0.0-beta.3 docker compose up -d docker compose exec altero altero user add docker compose exec altero altero user password

Starts PostgreSQL, altero, and persistent attachment storage. Image is published, no checkout or build needed.

How to configure altero

  • ALTERO_ACTIVITY_RETENTION_DAYS0How long delivered group activity is kept.

    How long delivered group activity is kept.

    values: integer (days), 0 = never

  • ALTERO_CONFIGPath to the configuration module, if not config.py at the repository root.

    Path to the configuration module, if not config.py at the repository root.

    values: filesystem path

  • ALTERO_DATABASE_MAX_OVERFLOW10Further connections opened when the pool is exhausted.

    Further connections opened when the pool is exhausted.

    values: integer

  • ALTERO_DATABASE_POOL_SIZE5Database connections kept open for requests to share.

    Database connections kept open for requests to share.

    values: integer

  • ALTERO_DATABASE_URLsqlite+aiosqlite:///altero.sqliteSQLAlchemy database URL. The driver must be an async one: sqlite+aiosqlite or postgresql+asyncpg.

    SQLAlchemy database URL. The driver must be an async one: sqlite+aiosqlite or postgresql+asyncpg.

    values: sqlite+aiosqlite:///altero.sqlite or postgresql+asyncpg://...

  • ALTERO_DEBUGfalseDebug behavior, SQL echoing and auto-reload. Never enable in production.

    Debug behavior, SQL echoing and auto-reload. Never enable in production.

    values: true/false

  • ALTERO_FORWARDED_ALLOW_IPSProxies whose X-Forwarded-For and X-Forwarded-Proto may be believed.

    Proxies whose X-Forwarded-For and X-Forwarded-Proto may be believed.

    values: comma-separated IPs or *

  • ALTERO_GROUP_DIGEST_INTERVAL60How often, in seconds, to look for activity that has settled. Zero turns group notifications off.

    How often, in seconds, to look for activity that has settled. Zero turns group notifications off.

    values: integer (seconds)

  • ALTERO_GROUP_DIGEST_QUIET_PERIOD900How long a group library must stop changing before what happened in it goes out.

    How long a group library must stop changing before what happened in it goes out.

    values: integer (seconds)

  • ALTERO_HOST127.0.0.1Interface to bind.

    Interface to bind.

    values: IP address or hostname

  • ALTERO_IMAGE_TAGlatestWhich published image to run: latest, a release such as 1.0.0-beta.3, or dev for master.

    Which published image to run: latest, a release such as 1.0.0-beta.3, or dev for master.

    values: version tag or dev

  • ALTERO_MAIL_FROMaltero@localhostFrom address on outgoing mail.

    From address on outgoing mail.

    values: email address

  • ALTERO_OPEN_REGISTRATIONfalseWhether anybody may register from the browser.

    Whether anybody may register from the browser.

    values: true/false

  • ALTERO_PASSWORD_RESETfalseWhether somebody who has forgotten their password may ask for a link.

    Whether somebody who has forgotten their password may ask for a link.

    values: true/false

  • ALTERO_PORT8000Port to bind.

    Port to bind.

    values: 1–65535

  • ALTERO_PUBLIC_URLThe stable external URL of the instance. Required for single sign-on, passkeys and the authorization server.

    The stable external URL of the instance. Required for single sign-on, passkeys and the authorization server.

    values: URL

  • ALTERO_PUBLISH_PORT8000Loopback port the API is published on.

    Loopback port the API is published on.

    values: integer

  • ALTERO_RATE_LIMIT0Requests allowed per API key, or per address when unauthenticated, in each window. Zero disables the limit.

    Requests allowed per API key, or per address when unauthenticated, in each window. Zero disables the limit.

    values: integer

  • ALTERO_RATE_LIMIT_WINDOW60Length of the rate limit window, in seconds.

    Length of the rate limit window, in seconds.

    values: integer

  • ALTERO_RETENTION_INTERVAL0How often, in seconds, to apply retention periods. Zero means only altero retention run ever does.

    How often, in seconds, to apply retention periods. Zero means only altero retention run ever does.

    values: integer (seconds)

  • ALTERO_SKIP_MIGRATIONSStarts the server without running alembic upgrade head first.

    Starts the server without running alembic upgrade head first.

    values: 1

  • ALTERO_SMTP_URLRelay for outgoing mail.

    Relay for outgoing mail.

    values: smtp://[user:password@]host[:port] or smtps://...

  • ALTERO_STORAGE_PATH./storageDirectory holding attachment files.

    Directory holding attachment files.

    values: filesystem path

  • ALTERO_TRASH_RETENTION_DAYS0How long an item stays in the trash before the server deletes it for good.

    How long an item stays in the trash before the server deletes it for good.

    values: integer (days), 0 = never

  • ALTERO_UPLOAD_RETENTION_HOURS24How long an authorized upload whose bytes never arrived is remembered.

    How long an authorized upload whose bytes never arrived is remembered.

    values: integer (hours)

  • POSTGRES_PASSWORDalteroPassword for the bundled PostgreSQL, used by both containers.

    Password for the bundled PostgreSQL, used by both containers.

    values: string

altero releases & version history

More in File Sync

Rate this project

Click to rate — no account needed.

0 comments

Posting as guest
Loading comments…