Deployment · s5w

Configuration

S5W is configured with environment variables only — there are no configuration files. No variable is required to run the storage; signing in to the panel needs two — a login and a password.

API and delivery :9090Panel :9091Data /var/lib/s5wApplied at container start

When changes take effect

Variables are read at start-up. For a change to take effect the container has to be recreated: docker compose up -d (compose recreates the changed service by itself) or docker rm -f s5w and a new docker run with the same volume. A plain docker restart is not enough — it does not change the container’s environment.

docker compose

docker-compose.yml
services:
  s5w:
    image: flytachi/s5w:latest
    ports:
      - "9090:9090"   # API and delivery
      - "9091:9091"   # admin panel
    environment:
      ADMIN_LOGIN: admin
      ADMIN_PASSWORD: ${S5W_ADMIN_PASSWORD}   # from .env next to the compose file
      PUBLIC_BASE_URL: https://files.example.com
      TIME_ZONE: Asia/Tashkent
      LOG_LEVEL: warning
    volumes:
      - s5w_data:/var/lib/s5w
    # The application stops first (up to 35 s), then PostgreSQL. Docker's default
    # 10 s is not enough: after a SIGKILL the database recovers on the next start.
    stop_grace_period: 45s
    restart: unless-stopped

volumes:
  s5w_data:

docker run

bash
docker run -d --name s5w -p 9090:9090 -p 9091:9091 \
  -e ADMIN_LOGIN=admin -e ADMIN_PASSWORD="$S5W_ADMIN_PASSWORD" \
  -e PUBLIC_BASE_URL=https://files.example.com \
  -e TIME_ZONE=Asia/Tashkent \
  -e LOG_LEVEL=warning \
  -v s5w_data:/var/lib/s5w \
  --stop-timeout 45 \
  --restart unless-stopped \
  flytachi/s5w:latest

All variables

Variable Default In short
ADMIN_LOGIN — Panel login. Without it, signing in is closed.
ADMIN_PASSWORD — Panel password. Without it, signing in is closed.
ADMIN_UI true Whether to run the panel on 9091.
ADMIN_API (from ADMIN_UI) Where the admin API is accepted from: local or public.
TRUSTED_PROXIES private Which proxies to believe in X-Forwarded-For.
PUBLIC_BASE_URL (request address) Base address of file links.
TIME_ZONE UTC Time zone of panel statistics when the browser did not report its own.
CACHE_DEFAULT_MAX_AGE 86400 Default max-age for the shared cache.
CACHE_PRIVATE_MAX_AGE 0 Default max-age for the private cache.
LOG_LEVEL info Log verbosity.
LOG_OUTPUT auto Where logs go.
SERVER_MEMORY_LIMIT 512M Memory ceiling of one process.
SERVER_REQUEST_TIMEOUT 30 Request processing timeout, seconds.
WINTER_KEY (generated) Signing key of sessions and temporary links.

The panel variables (ADMIN_*, TRUSTED_PROXIES) are covered in detail on the Admin panel page, the resource variables (SERVER_*) on Resources and performance. The rest are below.

No database settings

And none are needed: the database is bundled and the only one, and it listens only inside the container. No connection string, host or password has to be set — and an external database cannot be connected.

PUBLIC_BASE_URL

What it does. Sets the start of every address the service hands out itself: publicUrl and privateUrl of files, url of temporary links, addresses in the panel. Written with the scheme and without a trailing /: https://files.example.com.

If not set. The address is built from the incoming request: scheme, host and port. As long as clients reach the service directly, this works. Behind a proxy the result depends on which headers the proxy passes, and in the panel addresses are built from the panel’s domain — that is, they point to the wrong place. In that case the panel shows a warning.

When to set it. Always, if the service sits behind a proxy or the API has its own domain. Give the address where files are reachable from outside — the API domain, not the panel’s.

Links already handed out

Addresses are built at response time and stored nowhere. If you change PUBLIC_BASE_URL, new responses get the new address, while links already handed to clients keep the old domain — and work only as long as the old domain still points to the service.

TIME_ZONE

What it does. The time zone in which the panel splits traffic statistics into days when the browser did not report its own. Usually the browser does, and the variable is a fallback.

What it does not change. The maintenance schedule (always in UTC), the time in logs and the time in API responses — there it carries an explicit offset (+00:00).

Value. A name from the time zone database: Europe/Moscow, Asia/Tashkent. An unknown name is silently replaced with UTC.

CACHE_DEFAULT_MAX_AGE and CACHE_PRIVATE_MAX_AGE

What they do. The max-age values in the Cache-Control header used when neither the folder nor the bucket has its own max-age. CACHE_DEFAULT_MAX_AGE — for the shared cache (public: browser and CDN), a day by default. CACHE_PRIVATE_MAX_AGE — for the private one (private: browser only), 0 by default: the browser asks again every time, and ETag makes such a request cheap.

When to change them. To change the default behaviour for all buckets at once. For a single bucket or folder it is easier to set the policy in the panel — it takes precedence over these variables. How exactly the header is built is on the Caching page.

LOG_LEVEL and LOG_OUTPUT

LOG_LEVEL — debug · info · warning · error. The default is info: start and stop of the parts, bucket creation, errors. For a production server warning is usually enough. An empty value turns the application logs off.

LOG_OUTPUT — auto · stdout · stderr · file. auto means stdout, which is exactly what docker logs and any container log collector pick up. file writes to files inside the container; under Docker this is usually not needed.

PostgreSQL and s6 supervisor logs go to the same container output regardless of these variables.

WINTER_KEY

What it does. The secret key that signs panel sessions and /t/… temporary links.

If not set. On the first start the key is generated and saved in the volume, in /var/lib/s5w/.runtime_secrets, and read from there on later starts. Same volume, same key. The first start’s log shows this as WINTER_KEY auto-generated.

If set. The value from the environment takes precedence over the one saved in the volume. Set it if you want to keep the key in a secrets store rather than in the volume, or if you bring the service up on a new volume and want old temporary links to keep working.

If changed. Every panel session and every temporary link issued in every bucket stops working. Files, access tokens and public links are not affected.

The /var/lib/s5w volume

text
/var/lib/s5w/
  postgresql/        PostgreSQL cluster — metadata
  buckets/           files, one directory per bucket
  staging/           unfinished chunked uploads
  .runtime_secrets   the generated WINTER_KEY

Mount one volume as a whole. It has no parts that can be moved out separately: buckets/ and staging/ must be on the same filesystem (a chunked upload finishes with a rename, which is atomic only within one filesystem), and the database without the files or the files without the database are useless.

Without a volume the container is disposable. The data ends up in the container layer and disappears when the container is recreated — and it is recreated on every image update and every change of variables.

A host directory instead of a named volume is fine: -v /srv/s5w:/var/lib/s5w. The directory must be on a local filesystem: PostgreSQL on network filesystems such as NFS is unreliable.

The PostgreSQL version is tied to the volume. Currently it is 18. If the image ever moves to a new major version, the cluster will need pg_upgrade — this will be described separately in the release notes.

docker compose down -v

The -v flag deletes volumes, and with them the database and every file. To stop, use docker compose down without it, or docker compose stop.

Ports

The ports inside the container are fixed: 9090 — API and delivery, 9091 — the panel. For a different port outside, change only the publishing: -p 8080:9090. Behind a proxy on a shared docker network the ports need not be published at all — see Behind a reverse proxy.