This guide runs the Sikio image with Docker Compose on a Linux host, behind Caddy. Caddy obtains the TLS certificate. The last steps claim the instance and register your passkey. Operations documents every setting.
- A Linux host, amd64 or arm64, with Docker Engine and the Compose plugin.
- A domain name that resolves to the host, for example
sikio.example.org. Passkeys are bound to the domain they are created on, so use the final domain from the start. - Caddy on the same host, or another reverse proxy that terminates TLS and supports WebSockets.
The image is ghcr.io/oliverandrich/sikio. Each version is tagged X.Y.Z, X.Y and latest.
The X.Y tag receives patch releases, which contain no migrations. A new minor version may
contain migrations. Switch to it by changing the tag manually.
mkdir -p /opt/sikio && cd /opt/sikio
curl -fsSLO https://raw.githubusercontent.com/oliverandrich/sikio/main/docs/compose.yamlcompose.yaml runs the image with SQLite and stores its data in the volume
sikio-data. It publishes the port on the loopback only, for Caddy. Set PHX_HOST to your
domain.
SECRET_KEY_BASE signs sessions. Never change it after the first start.
umask 077
printf 'SECRET_KEY_BASE=%s\n' "$(openssl rand -base64 48 | tr -d '\n')" > sikio.envAdd further settings to the same file, one NAME=value per line. See
Operations. Keep sikio.env out of version control.
On a small host, these two settings lower memory use:
printf 'ERL_AFLAGS=+S 2:2\nRELEASE_MODE=interactive\n' >> sikio.envOperations explains what they do.
docker compose up -d
docker compose logs -f sikioOn the first start, the migration creates the SQLite database. The log line
Running SikioWeb.Endpoint means the server accepts requests. Check it from the host:
curl -fsS http://127.0.0.1:4000/healthsikio.example.org {
reverse_proxy 127.0.0.1:4000
}Caddy obtains the certificate and sends the client address in X-Forwarded-For. Inside the
container, connections come from the network gateway. That address is private, so Sikio trusts
it without TRUSTED_PROXIES. Reload Caddy and open https://sikio.example.org.
The first account requires a setup code. Issue it on the host:
docker compose exec sikio bin/setup-codeThe command prints the code once. Open your domain, enter the code, choose a username and create a passkey. Save the recovery codes on the next page, which are shown only once. If the code is lost, issue a new one. See Operations for details.
Further members join through invitations. Create them under Settings, then Invitations, in the gear menu.
The volume holds the SQLite database and the picture cache. Stop Sikio for a consistent copy:
docker compose stop sikio
docker run --rm -v sikio_sikio-data:/data -v "$PWD":/backup busybox \
tar -czf /backup/sikio-data-$(date +%F).tar.gz -C /data .
docker compose start sikioThe volume name is prefixed with the Compose project name. That is the directory name, here
sikio. docker volume ls lists it. Keep sikio.env with the backup. With a different
SECRET_KEY_BASE, all existing sessions become invalid.
Read the version's entry in the changelog first. Back up, then:
docker compose pull
docker compose up -d
docker compose logs -f sikioThe new version migrates the database on start. For a new minor version, change the tag in
compose.yaml before pulling. To go back, restore the backup. An older version does not revert
a newer version's migrations.
Sikio migrates a PostgreSQL database but does not create it. Create the database and its owner before the first start.
Add a database service and set Sikio's DATABASE_URL to it. On an empty volume, the postgres
image creates the database named in POSTGRES_DB. In compose.yaml:
services:
sikio:
# as before, plus:
environment:
SIKIO_DATABASE: postgres
DATABASE_URL: ecto://sikio:${POSTGRES_PASSWORD}@db/sikio
depends_on:
db:
condition: service_healthy
db:
image: postgres:18
restart: unless-stopped
environment:
POSTGRES_USER: sikio
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: sikio
volumes:
- sikio-db:/var/lib/postgresql
healthcheck:
test: ["CMD", "pg_isready", "-U", "sikio"]
interval: 10s
volumes:
sikio-data:
sikio-db:Set POSTGRES_PASSWORD= to a long random value in a file .env beside compose.yaml.
Compose reads it to substitute ${…}. Sikio's volume then holds only the picture cache. Back up
the database with pg_dump:
docker compose exec db pg_dump -U sikio sikio | gzip > sikio-$(date +%F).sql.gzA host may run one PostgreSQL server and one Caddy for several services. Each is attached to
its own Docker network. Here the database server is postgres on the network db. Caddy is on
the network caddy and reads its configuration from container labels. The administrator creates
a database and an owner for Sikio:
CREATE ROLE sikio LOGIN PASSWORD '…';
CREATE DATABASE sikio OWNER sikio;Sikio is attached to both networks and publishes no port:
services:
sikio:
image: ghcr.io/oliverandrich/sikio:0.1
restart: unless-stopped
env_file: sikio.env
environment:
PHX_HOST: sikio.example.org
SIKIO_DATABASE: postgres
DATABASE_URL: ecto://sikio:${SIKIO_DB_PASSWORD}@postgres/sikio
labels:
caddy: sikio.example.org
caddy.reverse_proxy: "{{upstreams 4000}}"
networks: [db, caddy]
volumes:
- sikio-data:/data
volumes:
sikio-data:
networks:
db:
external: true
caddy:
external: trueCaddy connects from its private address on the caddy network, which needs no
TRUSTED_PROXIES. The volume holds only the
picture cache. Sikio downloads missing pictures again. Database backups are the administrator's
responsibility. Issue the setup code as in step 5.
Operations describes the conversion. In a container, run
the command in a one-off container of the sikio service while the server is stopped. Back up
first.
From SQLite to PostgreSQL, add the database service as in
Its own database container, but leave the sikio service on
SQLite. Then convert:
docker compose stop sikio
docker compose up -d --wait db
read -rs TARGET_DATABASE_URL # ecto://sikio:PASSWORD@db/sikio
export TARGET_DATABASE_URL
docker compose run --rm -e TARGET_DATABASE_URL sikio /app/bin/convert-to-postgres-e TARGET_DATABASE_URL without a value passes the variable from the shell, so the password
stays off the command line.
Then add SIKIO_DATABASE and DATABASE_URL to the
sikio service and run docker compose up -d.
From PostgreSQL to SQLite, convert into a new file in the volume:
docker compose stop sikio
docker compose run --rm sikio /app/bin/convert-to-sqlite /data/sikio.dbThe image sets DATABASE_PATH=/data/sikio.db. If an older file exists there, move it away
first. The command never writes over a file. Then remove SIKIO_DATABASE and DATABASE_URL
from the sikio service and run docker compose up -d.