Skip to main content

Install with Docker


You can install Bag of words with a single docker command. By default, it will use SQLite as the database. You can also configure it to use PostgreSQL by passing BOW_DATABASE_URL environment variable.
docker run --pull always -d -p 3000:3000 bagofwords/bagofwords
To use PostgreSQL, you need to set the BOW_DATABASE_URL environment variable. For example: BOW_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/bagofwords

Update

  • Re-run the same docker run --pull always ... command to fetch and start the latest image.
  • Optionally, pull explicitly and restart:
    docker pull bagofwords/bagofwords:latest
    # stop/remove your existing container if needed, then start again
    # docker stop <container_name> && docker rm <container_name>
    docker run --pull always -d -p 3000:3000 bagofwords/bagofwords
    

Install with Docker Compose


Run Bag of words with Docker Compose and Caddy (built-in TLS on port 443). We recommend using the canonical files from the repo to avoid drift:

Steps

  1. Make sure Docker and Docker Compose are installed.
  2. Clone the repo:
    git clone https://github.com/bagofwords1/bagofwords
    cd bagofwords
    
  3. Create a .env file (for domain and credentials). Example:
    # Domain used by Caddy for HTTPS (must resolve to your server's public IP)
    DOMAIN=yourdomain.com
    
    # PostgreSQL (use stronger values for production)
    POSTGRES_USER=bow
    POSTGRES_PASSWORD=your_secure_pw
    POSTGRES_DB=bagofwords
    
    # Optional but recommended: encryption key (Fernet, 44 chars incl. '=')
    # Generate with OpenSSL: openssl rand -base64 32 | tr '+/' '-_'
    BOW_ENCRYPTION_KEY=
    
    Generate BOW_ENCRYPTION_KEY with OpenSSL:
    openssl rand -base64 32 | tr '+/' '-_'
    
  4. Start services:
    docker compose up -d
    
  5. Point your domain to the server’s public IP:
    • Create an A record for yourdomain.com → your instance public IP.
    • Caddy will automatically obtain/renew the TLS certificate and serve on port 443.
  6. Open https://yourdomain.com
Caddy is included by default as a reverse proxy on port 443. If you prefer to run without Caddy, remove the caddy service from docker-compose.yaml and expose the app directly on port 3000. For local, no-SSL testing, you can also use docker-compose.dev.yaml.

Update

# pull latest images and recreate containers
docker compose pull
docker compose up -d

Other configurations


You can also configure additional settings in the bow-config.yaml file.
# bow-config.yaml
# Deployment Configuration
base_url: http://0.0.0.0:3000 

database:
  url: ${BOW_DATABASE_URL}

# Feature Flags
features:
  allow_uninvited_signups: false
  allow_multiple_organizations: false 
  verify_emails: false

google_oauth:
  enabled: false
  client_id: ${BOW_GOOGLE_CLIENT_ID}
  client_secret: ${BOW_GOOGLE_CLIENT_SECRET}

smtp_settings:
  host: "smtp.resend.com"
  port: 587
  username: "resend"
  password: ${BOW_SMTP_PASSWORD}

encryption_key: ${BOW_ENCRYPTION_KEY}

intercom:
  enabled: true

To use the custom config file, you can run the following command:
docker run --pull always -d -p 3000:3000 -v $(pwd)/bow-config.yaml:/app/bow-config.yaml bagofwords/bagofwords

Install with Kubernetes


You can install Bag of words on a Kubernetes cluster. The following deployment will deploy the Bagofwords container alongside a postgres instance.

1. Add the Helm Repository

helm repo add bow https://helm.bagofwords.com/
helm repo update

2. Install or Upgrade the Chart

Here are a few examples of how to install or upgrade the Bag of words Helm chart:
helm upgrade -i --create-namespace \
 -nbowapp-1 bowapp bow/bagofwords \
 --set postgresql.auth.username=<PG-USER> \
 --set postgresql.auth.password=<PG-PASS> \
 --set postgresql.auth.database=<PG-DB>
# deploy without TLS with custom hostname
helm upgrade -i --create-namespace \
 -nbowapp-1 bowapp bow/bagofwords \
  --set host=<HOST> \
 --set postgresql.auth.username=<PG-USER> \
 --set postgresql.auth.password=<PG-PASS> \
 --set postgresql.auth.database=<PG-DB> \
 --set ingress.tls=false
# deploy with TLS, certs by cert manager and Googole oauth enabled 
helm upgrade -i --create-namespace \
 -nbowapp-1 bowapp bow/bagofwords \
 --set host=<HOST> \
 --set postgresql.auth.username=<PG-USER> \
 --set postgresql.auth.password=<PG-PASS> \
 --set postgresql.auth.database=<PG-DB>
 --set config.googleOauthEnabled=true \
 --set config.googleClientId=<CLIENT_ID> \
 --set config.googleClientSecret=<CLIENT_SECRET>

Update

# Restart the Bag of words deployment(s) to pick up the latest image
# Adjust namespace (-n) and selector if you used different names
kubectl rollout restart deployment -n bowapp-1 -l app.kubernetes.io/instance=bowapp
kubectl rollout status deployment -n bowapp-1 -l app.kubernetes.io/instance=bowapp

Google OAuth

To enable Google OAuth authentication, configure the following parameters in your bow config (or in env/k8s configmap)
google_oauth:
  enabled: true
  client_id: ${BOW_GOOGLE_CLIENT_ID}
  client_secret: ${BOW_GOOGLE_CLIENT_SECRET}
You should also set the following in your Google OAuth configurations
  1. Callback URL: https://yourbaseurl.com/api/auth/google/callback
  2. Scopes: /auth/userinfo.email, /auth/userinfo.profile, openid
  3. Enable People API

OpenID Connect (OIDC)

oidc_providers:
- name: okta
  enabled: true
  issuer: https://***********.okta.com/oauth2/default
  client_id: ${OKTA_CLIENT_ID}
  client_secret: ${OKTA_CLIENT_SECRET}
  scopes: ["openid", "profile", "email"]
  pkce: true
  client_auth_method: basic
  discovery: true
  uid_claim: sub

For Okta

  1. Set a new OIDC application: web
  2. Set callback URL `https://your-base-bow-url.com/api/auth/call