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
- Make sure Docker and Docker Compose are installed.
- Clone the repo:
git clone https://github.com/bagofwords1/bagofwords
cd bagofwords
- 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 '+/' '-_'
- Start services:
- 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.
- 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
- Callback URL:
https://yourbaseurl.com/api/auth/google/callback
- Scopes:
/auth/userinfo.email, /auth/userinfo.profile, openid
- 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
- Set a new OIDC application: web
- Set callback URL `https://your-base-bow-url.com/api/auth/call