# ===================================================== # Skiff — one-command self-hosted deploy # # Usage: # mkdir -p data # cp .env.example .env # # edit .env and set SKIFF_COOKIE_SECRET # docker compose up -d --build # # Then visit http://localhost:8080 (or your reverse proxy). # # PORT BINDING SECURITY: # By default Skiff binds to 127.0.0.1 only. This means it is # NOT reachable from other machines on the network, which is the # safe default — especially important before the vault is initialized, # since /api/vault/setup is unauthenticated (first caller sets the # master password). # # To expose on a LAN/public IP, set HOST_ADDR in your .env: # HOST_ADDR=0.0.0.0 # all interfaces # HOST_ADDR=192.168.1.x # specific NIC # Then put Skiff behind a reverse proxy (nginx/Caddy) with TLS. # # DATA DIRECTORY: # Create ./data as your own user before first run. If Docker # creates it, it will be owned by root and the container's # unprivileged node user cannot write the database. # ===================================================== services: skiff: build: . image: skiff:latest container_name: skiff restart: unless-stopped ports: # FIX: bind to 127.0.0.1 by default so the setup endpoint is not # reachable from the network before the vault is initialized. # Override by setting HOST_ADDR=0.0.0.0 in .env for reverse-proxy setups. - "${HOST_ADDR:-127.0.0.1}:${HOST_PORT:-8080}:8080" volumes: - ./data:/app/data environment: SKIFF_PORT: 8080 SKIFF_HOST: 0.0.0.0 SKIFF_DATA_DIR: /app/data SKIFF_COOKIE_SECRET: "${SKIFF_COOKIE_SECRET:?required - generate with: openssl rand -hex 32}" SKIFF_TRUSTED_ORIGINS: ${SKIFF_TRUSTED_ORIGINS:-} SKIFF_SETUP_TOKEN: ${SKIFF_SETUP_TOKEN:-} SKIFF_TRUST_PROXY: ${SKIFF_TRUST_PROXY:-} SKIFF_MAX_SESSIONS_PER_USER: ${SKIFF_MAX_SESSIONS_PER_USER:-} NODE_ENV: production healthcheck: test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:8080/api/health').then(r=>r.ok?process.exit(0):process.exit(1)).catch(()=>process.exit(1))"] interval: 30s timeout: 5s retries: 3 start_period: 5s