amfora.

THE FIELD GUIDE

From first install
to final handoff.

Practical documentation for running Amfora and using it to send, collect and manage files.

01 / Start here

A private file workspace you operate

Amfora keeps file metadata in SQLite and stores objects through an S3 compatible storage layer. The image includes a private MinIO service for a zero configuration starting point, and can use an external S3 compatible provider when that better fits your infrastructure.

Send

Upload files, create a share, and send a browser link. Add a password, expiry or view limit, and send recipient email notifications.

Receive

Publish a receive link so people can upload without an Amfora account. Set limits before sharing it.

Control

Manage users and groups, folders, the trash, links, branding, two factor authentication, passkeys, and optional OAuth2/OIDC providers.

02 / Install

Start with Docker Compose

You need Docker with Compose v2 and a running Docker engine. Run this command from a directory without an existing amfora folder:

curl -fsSL https://amfora.solutionmax.net/get | sh

The installer script checks Docker and Compose, creates an amfora directory with a compose file pinned to the current release, pulls ghcr.io/solutionmax/amfora (amd64 and arm64) and starts it. Existing Amfora directories, containers or data volumes are refused. It does not install Docker or configure HTTPS.

To inspect the script before running it:

curl -fsSL https://amfora.solutionmax.net/get -o amfora-get.sh
less amfora-get.sh
sh amfora-get.sh

To build the release yourself, add --source: the installer clones the release tag and builds it, which also needs Git, Bash and Buildx 0.30 or later. The build reuses layers in a dedicated Amfora builder and keeps unused cache near a 4 GB target; make clean only reclaims build cache.

Open http://localhost:5487. The first start creates the SQLite database, seeds configuration, and makes the first account created on an empty database an administrator. Later accounts are ordinary users unless an administrator creates or promotes them.

The sample Compose file publishes the web interface on 5487 and bundled storage on 9379. The API listens on 3333 inside the container and is left unpublished because the bundled web app reaches it through its server side proxy.

For access from another computer, change STORAGE_URL from localhost to the storage address that computer can reach. For public use, complete the HTTPS setup below before sharing the instance.

03 / Production

Give the app and storage clear HTTPS addresses

Use a trusted reverse proxy for both the web interface and the browser facing storage endpoint. A split domain deployment can look like this:

https://app.example.com    → container port 5487
https://files.example.com  → container port 9379

Set the values that describe that public arrangement:

environment:
  SECURE_SITE: "true"
  STORAGE_URL: "https://files.example.com"

STORAGE_URL is the URL used by a browser, not the address one container uses to reach another. The storage hostname must forward to the internal storage service and accept the browser’s upload and download requests. Keep the API on the private container network unless you operate another authenticated API client.

A minimal Caddy example

For Caddy running on the same host, bind the application ports to localhost in Compose. Replace the domains below with your own and point their DNS records at that host.

ports:
  - "127.0.0.1:5487:5487"
  - "127.0.0.1:9379:9379"
app.example.com {
    reverse_proxy 127.0.0.1:5487
}
files.example.com {
    reverse_proxy 127.0.0.1:9379
}

The product website at amfora.solutionmax.net is separate from your Amfora application. These example domains do not provide a hosted Amfora service.

Forward the original host and protocol through the proxy. SECURE_SITE=true enables secure cookies. Configure HSTS at your HTTPS reverse proxy. Set TRUST_PROXY=false when the API is exposed without a trusted proxy in front of it.

04 / Storage

Choose bundled or external S3 compatible storage

Bundled storage

Leave ENABLE_S3=false (the default) to run the included MinIO service. Set STORAGE_URL to the public URL that browsers can reach. The Compose example uses http://127.0.0.1:9379 for local use. The bucket is private and the container writes its generated credentials under the persistent application directory.

External storage

Set ENABLE_S3=true and provide the external endpoint and credentials. The endpoint and bucket must be reachable and permitted for the browser requests represented by generated upload and download URLs.

ENABLE_S3=true
S3_ENDPOINT=s3.example.com
S3_PORT=443
S3_USE_SSL=true
S3_ACCESS_KEY=your-access-key
S3_SECRET_KEY=your-secret-key
S3_REGION=us-east-1
S3_BUCKET_NAME=amfora-files
S3_FORCE_PATH_STYLE=false

Use S3_FORCE_PATH_STYLE=true for providers such as MinIO. If a provider rejects the SDK checksum, set S3_DISABLE_CHECKSUMS=true as documented by that provider. Keep S3_REJECT_UNAUTHORIZED=true unless you have a deliberate certificate exception.

Limits and URL lifetime

A new installation seeds a 1 GiB maximum file size and 10 GiB maximum storage per user. Administrators can adjust those values in Settings, and set a different storage limit for one user in the user form on the Users page. PRESIGNED_URL_EXPIRATION controls the default generated storage URL lifetime in seconds and defaults to 3600.

05 / Daily work

Send a link or collect files through a form

Your workspace

The application opens at sign in, or your dashboard when you are already signed in. The dashboard gives you a starting point for files, shares and upload requests. Use folders to organize files before creating a share. Profile manages your personal details and account security; administration controls are available to administrators.

Send files

  1. Sign in and open My files.
  2. Upload files or create folders.
  3. Select the items, create a share, and set its restrictions.
  4. Copy the generated /s/<alias> link.

A share can have a password, expiration date, maximum view count, and recipient email notifications. Recipient entries do not restrict access to a public share. Its owner can later edit the contents, notification recipients, password, expiry, or alias from Shares. A recipient opens the link in a browser; a protected share asks for its password before files are available.

Copy a link or export its QR code

Use the share menu to copy its link. Open the QR code action to display or download a PNG that points to the same URL. After changing an alias or application hostname, generate a new QR code and replace any older copies. Anyone with the link or code can reach the share, subject to its restrictions.

Receive files

  1. Open Receive files and create a receive share.
  2. Choose optional file count, size, type, password, expiry, name, and email requirements.
  3. Copy the generated /r/<alias> link.

The sender uploads from the public receive page without an Amfora account. The owner reviews the received files in Reverse shares.

Share a secret

  1. Open Secrets and choose New.
  2. Paste the password or key, choose how long the link lives and how often it may be opened, and set a passphrase if you want one.
  3. Copy the /x/<id>#<key> link straight away. It is shown once.

The text is encrypted in your browser. The key is the part of the link behind the #, which a browser never sends to a server, so nobody can read the secret without the link, also not an administrator. After the last opening, or when its time runs out, the text is destroyed. Three wrong passphrases destroy it too.

The reader presses one button to see the secret. Loading the page costs nothing, so a chat app that fetches the link for a preview cannot use it up.

An administrator can open /secret to visitors without an account, with its own limits, under Settings, Security. It is off by default. A full access API key can store secrets too; the sealing recipe and test vectors are in SECRETS.md.

See what happened

Activity lists what happened to your links and your account: a share opened or downloaded, a wrong password, files received, a secret opened, a sign in. A user sees their own links, an administrator sees everyone, and the list can be exported as CSV. It is kept for 90 days unless you change that.

Each line can say where a visitor was. The address itself is never stored. Places need a database you supply in MaxMind format, for example the free DB-IP City Lite; without it only your own network is recognised. See ACTIVITY.md for the file location and for running behind a reverse proxy.

On a share you can ask for an email when it is downloaded, and on a share or receive link for a reminder three days before it ends. Both need outgoing email. Under Settings, Security you can also give one webhook address that is called, signed, when files are received, a share is downloaded or a secret is opened.

06 / Administration

Keep access and identity in the workspace

Administrators can invite users, list and deactivate accounts, change roles, set storage limits, customize the installation, configure SMTP, and manage external login providers. Invite links are one time registration links.

Make it feel like your brand

Open Customization to set the application name, description, logo, accent color, font and corner radius. All of it is stored on the server, so every visitor sees the same brand; the logo also replaces the mark in link previews and the favicon. Check the result in both light and dark themes, on mobile, and on a public upload and download page. Use Settings for storage limits, language, email and authentication configuration.

Brandpack: run it under your own name

Free installations show "Powered by Amfora" on the public pages. A brandpack is a signed key that removes that credit and unlocks two more settings: a background image for the public pages and custom CSS. Paste the key under Customization → Brandpack and press Activate; the installation verifies the signature itself with a public key that ships in the source, so nothing has to reach SolutionMAX for it to work. Removing the key brings the credit back and keeps the stored background and CSS for the next activation.

Custom CSS is admin-only and sanitised on the server: imports, external URLs, expressions and behaviours are removed and the size is capped at 20 kB. The background image is converted to WebP, resized to 2400 px wide and served from /api/app/background. Details for operators and vendors are in docs/BRANDPACK.md.

Two factor authentication

Each user can enable TOTP in their profile, verify setup with an authenticator, save backup codes, and remove trusted devices. A 2FA login verifies the password first and then accepts a TOTP or unused backup code. Users can also add passkeys on their profile and sign in with them. See Sign in security for requiring a second step.

OAuth2 and OIDC

External login is optional. A new installation offers Authentik, GitHub and Google. Any other compatible OIDC or OAuth 2.0 provider can be added with Add provider. Match callback URLs and client settings at both ends. Keep password authentication enabled until an external provider has been tested; disabling it also disables password reset.

Password recovery

Password reset requires password authentication and a working SMTP configuration. Responses deliberately do not reveal whether an email address exists. For operator recovery, the container includes an interactive reset tool:

docker compose exec amfora sh -lc 'cd /app/amfora-app && ./reset-password.sh'
docker compose exec amfora sh -lc 'cd /app/amfora-app && ./reset-password.sh --list'

Download page cover and link previews

Under Customization, Download page, upload one image to use as the cover at the top of every download page. The same image is the preview in WhatsApp, Slack and similar apps; a separate default link preview image is used when no cover is set. Files from a share are never used for either.

Download pages do not play video and audio. Visitors see no play or Preview button for those files and the server refuses to stream them; they download instead. Images, PDFs and text keep their preview.

07 / Version 2.4

Trash, groups, scanning and monitoring

The settings and options an administrator meets in Amfora 2.4. Everything here is optional or has a default that changes nothing for an existing installation.

Trash and clean up

Deleting a file or folder in the workspace moves it, with everything in it, to the Trash page. Restore puts it back where it was. Delete for good and Empty trash are the only places where files leave storage. Deleted items show nowhere else and count toward the storage limit. Everybody sees only their own trash.

Under Settings, Storage:

  • Trash retention (trashRetentionDays): days an item stays in the trash before it is deleted for good. Once a day the server cleans up. Default 30, at least 1.
  • Remove ended links (expiredLinkRetentionDays): days after which a share or receive link that has ended is removed. Default 0, which means never. Only the link goes: its files stay in the workspace. A receive link that holds files stays. Each removal is written to the activity log.
  • Default and maximum lifetime of links, in whole days, 0 for none. New shares and receive links start with the default end date and cannot go beyond the maximum. The server refuses a longer date, also for API keys.

Sign in security

Under Settings, Security, choose whether administrators, everyone or nobody must set up two step sign in or a passkey. Turning it on signs nobody out. A user who has neither is taken to a set up page and cannot use anything else until it is done. API keys and users who sign in through an external provider are not affected.

An administrator can switch off the two step sign in of another user on the Users page (passkeys stay). Passkeys need an https address or localhost.

If the only administrator loses the authenticator while the requirement is on, start the server once with TWO_FACTOR_REQUIRED=off. It wins over the setting, so you can sign in and repair the account. Remove the variable and recreate the container afterwards with docker compose up -d.

Groups

On the Users page, the Groups tab lets an administrator create, rename and delete groups and add or remove members. A share can be limited to one group under “Who can open it”. It then opens only for a signed in member, its maker and administrators. Members find such shares under Shared with me on the Shares page. Removing somebody from a group takes the access away at once. A group that shares still use cannot be deleted.

Monitoring

GET /api/v1/metrics answers in the Prometheus text format: version, database and storage up, users, files and bytes, the trash, shares, receive links, secrets, the data disk and the virus scan. Only the API key of an administrator opens it. The answer is cached for 30 seconds, so scrape every 30 to 60 seconds.

curl -s https://files.example.com/api/v1/metrics \
  -H "Authorization: Bearer amf_..."

Examples for Prometheus and Zabbix are in MONITORING.md.

Virus scan

Set CLAMAV_HOST to the address of a ClamAV scanner (clamd) and Amfora checks every uploaded file after the upload, one at a time, also files received on a receive link. While a file is being checked or is infected nobody can download, preview or share it; its owner can only delete it. An infected file is written to the owner’s activity log, shown in the bell and mailed to the owner and the administrators. Without CLAMAV_HOST nothing changes. ClamAV needs about 1.2 GB of memory.

services:
  amfora:
    environment:
      CLAMAV_HOST: "clamav"
  clamav:
    image: clamav/clamav:stable
    environment:
      CLAMD_CONF_StreamMaxLength: "100M"
    volumes:
      - clamav_data:/var/lib/clamav
    mem_limit: 2g
volumes:
  clamav_data:

The full recipe, the retry times and what each status means are in VIRUS-SCAN.md.

Languages

Amfora comes in English, Dutch, German, French, Spanish, Italian, Portuguese (Brazil) and Polish. Users pick their language in the language picker; the default is set under Settings. Italian, Portuguese and Polish were translated without review by a native speaker.

Environment variables added in 2.4

  • TWO_FACTOR_REQUIRED: off, admins or all. Wins over the setting in Settings. Use it to get back in after a lockout, then remove it.
  • CLAMAV_HOST: address of clamd. Unset means no virus scan.
  • CLAMAV_PORT: the TCP port of clamd, default 3310.
  • CLAMAV_MAX_SIZE_MB: files above this size are not scanned and show as “Not checked”. Default 100.

08 / Operate

Back up before you upgrade

Bundled storage backups

Back up the complete persistent /app/server directory, including prisma/amfora.db, minio-data, and generated storage credentials. Stop the container or use a filesystem snapshot consistent for the database and objects.

External storage backups

Back up the SQLite database and the external S3 bucket through that provider’s supported process. Keep the database and object backup points close enough to restore a consistent workspace.

Updating

Your files and settings live in the persistent volume; an update only replaces the image. In the amfora directory, set the new version in docker-compose.yaml and run:

docker compose pull
docker compose up -d

Startup applies the Prisma schema and seeds missing configuration and provider records. Older database filenames are migrated to prisma/amfora.db; an existing internal bucket is preserved when no explicit bucket override is supplied. Verify a test share and download afterwards, and keep the previous image until the new one runs so you can switch back.

The first start of 2.4.0 adds the tables and columns for the trash, scan status, passkeys, groups and the storage limit per user, by itself. Going back to an older version needs the database backup from before the upgrade.

The admin area shows when a newer release is out. To let the host install signed releases for you with one click, set up over the air updates once. Every release is on the releases page.

09 / Troubleshooting

Check the boundary where the request stops

The page loads but uploads fail.
Confirm that STORAGE_URL is browser reachable, forwards to the storage service, and accepts the storage requests. Then inspect docker compose logs -f amfora.
The API starts but storage is unavailable.
For bundled storage, wait for initialization and check for .minio-credentials in the persistent directory. For external S3, verify every S3_* value and bucket permission.
HTTPS redirects or cookies behave incorrectly.
Check the forwarded host and protocol, set SECURE_SITE=true for HTTPS, and set TRUST_PROXY=false when no trusted proxy is present.
Password reset email does not arrive.
Enable SMTP in Settings, verify the host, port, credentials, sender, and TLS mode, and run the admin SMTP test action.
An upgrade appears empty.
Check that the original persistent volume is mounted. Do not create a new bucket or rename the storage directory by hand; preserve the bucket recorded in .minio-credentials.

10 / About Amfora

A self hosted workspace for the handoff.

Amfora is a self hosted file workspace for sending files, collecting uploads, and managing access. This guide covers the installation, storage, sharing, and administration workflows that keep a handoff moving.

The application source is publicly available on GitHub. No account or access request is required. Amfora has its own visual identity and ongoing improvements. Its upstream attribution is preserved in NOTICE. It is licensed under the Apache License 2.0. Bundled MinIO and mc are separate, unmodified AGPL-3.0 programs. See LICENSE, NOTICE and the bundled program license.

Fonts: Bricolage Grotesque and Plus Jakarta Sans, distributed under the SIL Open Font License. Font license files are included with the source repository.