Self-hosting

Run Tributary on your own machine with Docker. Your files stay in your Google accounts.

What you need

  • A machine with Docker and Docker Compose. The images are built for 64-bit Intel and AMD (amd64).
  • A Google account to create a free Google Cloud project, which is what lets Tributary reach Drive.
  • If you want to use it from other devices, a domain name and HTTPS.

Start it

Make a folder, then save this as docker-compose.yml inside it, or download the file.

# Tributary. Put this beside your .env file, then run: docker compose up -d
name: tributary

services:
  # The address browsers use. It serves the app and passes API calls to the api service.
  web:
    image: swayam5342/tributary-web:0.1.0
    environment:
      DEXTRO_API_URL: http://api:8080
    ports:
      # Only this machine can reach it. Put your HTTPS proxy in front of this port.
      - 127.0.0.1:${PORT:-3000}:3000
    networks:
      default:
        # Fixed so the api service can trust the visitor's address from this one alone.
        ipv4_address: 172.28.77.250
    depends_on:
      - api
    read_only: true
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    restart: unless-stopped

  api:
    image: swayam5342/tributary-api:0.1.0
    environment:
      DEXTRO_DATABASE_URL: postgres://tributary:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}@postgres:5432/tributary?sslmode=disable
      DEXTRO_PUBLIC_URL: ${PUBLIC_URL:?set PUBLIC_URL in .env}
      DEXTRO_TRUSTED_PROXIES: 172.28.77.250
      DEXTRO_GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-}
      DEXTRO_GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-}
      DEXTRO_GOOGLE_PICKER_API_KEY: ${GOOGLE_PICKER_API_KEY:-}
      DEXTRO_ENCRYPTION_KEYS: ${ENCRYPTION_KEYS:?set ENCRYPTION_KEYS in .env}
      DEXTRO_ENCRYPTION_KEY_ID: ${ENCRYPTION_KEY_ID:-}
    depends_on:
      postgres:
        condition: service_healthy
    read_only: true
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    restart: unless-stopped

  postgres:
    image: postgres:18-alpine
    environment:
      POSTGRES_USER: tributary
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
      POSTGRES_DB: tributary
    volumes:
      - postgres-data:/var/lib/postgresql
    healthcheck:
      test: [CMD-SHELL, pg_isready -U tributary -d tributary]
      interval: 3s
      timeout: 3s
      retries: 20
    restart: unless-stopped

networks:
  default:
    ipam:
      config:
        - subnet: 172.28.77.0/24

volumes:
  postgres-data:

Create a .env file beside it, with its own random secrets:

cat > .env <<EOF
PUBLIC_URL=http://127.0.0.1:3000
POSTGRES_PASSWORD=$(openssl rand -hex 24)
ENCRYPTION_KEYS=k1:$(openssl rand -base64 32)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
EOF

Keep a copy of ENCRYPTION_KEYS somewhere safe. Without it, every linked account has to be set up again.

Start it:

docker compose up -d

Open http://127.0.0.1:3000. The first account you create becomes the admin, and after that only the admin can add people.

Connect Google

Tributary uses your own Google project, so nothing passes through anyone else's. In the Google Cloud console:

  1. Create a project and enable the Google Drive API.
  2. On the consent screen, add the drive.file scope.
  3. Create an OAuth client of type Web application.
  4. Add these redirect addresses, using your PUBLIC_URL:
http://127.0.0.1:3000/api/v1/auth/google/callback
http://127.0.0.1:3000/api/v1/storage/google/callback

Put the client ID and secret in .env, then run docker compose up -d again. You can now link accounts on the Storage page.

While the consent screen is in Testing, every account you link has to be listed as a test user, and its sign-in expires after 7 days.

Use it from other devices

Sign-in cookies only work over HTTPS, apart from on 127.0.0.1, so a plain address on your network won't keep you signed in. Put a reverse proxy such as Caddy or nginx in front of port 3000, then set PUBLIC_URL to the address people use, such as https://files.example.com, and update Google's redirect addresses to match.

Admin commands

Run these on the server, from the folder with your compose file:

docker compose exec api /tributary-cli help
docker compose exec api /tributary-cli status
statusAccounts, usage and when each background job last ran
usersList the accounts on this server
create-userAdd someone
reset-passwordSet a new password, which they'll have to replace
delete-userRemove an account. Their files stay in their Drive
transfer-adminHand the admin role to someone else
syncCheck every linked account against Drive now
linksList the public Google links on this server
upload-limitShow or change the largest upload allowed

Update and back up

To update, change the version in docker-compose.yml, then run:

docker compose pull
docker compose up -d

Database changes apply by themselves when the server starts.

Your files are safe in Drive, but the trash, links, users and keys live in the database, so back that up too:

docker compose exec -T postgres pg_dump -U tributary tributary > tributary.sql

If the database is ever lost, a sync brings your files and folders back from Drive. The trash, users and keys don't come back.

Ready to build on it? Read the API guide, or see the code on GitHub.