From 4218f2e161c22d4745d7b47f8349262bac923c35 Mon Sep 17 00:00:00 2001 From: Kevin McConnell Date: Thu, 11 Dec 2025 15:37:47 +0000 Subject: [PATCH] Document Docker image deployment Also split the README a bit, to move the details under /docs. --- README.md | 190 ++------------------------------------ config/deploy.yml | 2 +- docs/development.md | 80 ++++++++++++++++ docs/docker-deployment.md | 166 +++++++++++++++++++++++++++++++++ docs/kamal-deployment.md | 111 ++++++++++++++++++++++ 5 files changed, 366 insertions(+), 183 deletions(-) create mode 100644 docs/development.md create mode 100644 docs/docker-deployment.md create mode 100644 docs/kamal-deployment.md diff --git a/README.md b/README.md index 5ea839008..867c2dba7 100644 --- a/README.md +++ b/README.md @@ -2,202 +2,28 @@ This is the source code of [Fizzy](https://fizzy.do/), the Kanban tracking tool for issues and ideas by [37signals](https://37signals.com). -## Deploying Fizzy -If you'd like to run Fizzy on your own server, we recommend deploying it with [Kamal](https://kamal-deploy.org/). -Kamal makes it easier to set up a bare server, copy the application to it, and manage the configuration settings that it uses. +## Running your own Fizzy instance -(Kamal is also what we use to deploy Fizzy at 37signals. If you're curious about what our deployment configuration looks like, you can find it inside [`fizzy-saas`](https://github.com/basecamp/fizzy-saas).) +If you want to run your own Fizzy instance, but don't need to change its code, you can use our pre-built Docker image. +You'll need access to a server on which you can run Docker, and you'll need to configure some options to customize your installation. -This repo contains a starter deployment file that you can modify for your own specific use. That file lives at [config/deploy.yml](config/deploy.yml), which is the default place where Kamal will look for it. +You can find the details of how to do a Docker-based deployment in our [Docker deployment guide](docs/docker-deployment.md). -The steps to configure your very own Fizzy are: +If you want more flexibility to customize your Fizzy installation by changing its code, and deploy those changes to your server, then we recommend you deploy Fizzy with Kamal. You can find a complete walkthrough of doing that in our [Kamal deployment guide](docs/kamal-deployment.md). -1. Fork the repo -2. Edit few things in config/deploy.yml and .kamal/secrets -3. Run `kamal setup` to do your first deploy. - -We'll go through each of these in turn. - -### Fork the repo - -To make it easy to customise Fizzy's settings for your own instance, you should start by creating your own GitHub fork of the repo. -That allows you to commit your changes, and track them over time. -You can always re-sync your fork to pick up new changes from the main repo over time. - -Once you've got your fork ready, run `bin/setup` from within it, to make sure everything is installed. - -### Editing the configuration - -The config/deploy.yml has been mostly set up for you, but you'll need to fill out some sections that are specific to your instance. -To get started, the parts you need to change are all in the "About your deployment" section. -We've added comments to that file to highlight what each setting needs to be, but the main ones are: - -- `servers/web`: Enter the hostname of the server you're deploying to here. This should be an address that you can access via `ssh`. -- `ssh/user`: If you access your server a `root` you can leave this alone; if you use a different user, set it here. -- `proxy/ssl` and `proxy/host`: Kamal can set up SSL certificates for you automatically. To enable that, set the hostname again as `host`. If you don't want SSL for some reason, you can set `ssl: false` to turn it off. -- `env/clear/MAILER_FROM_ADDRESS`: This is the email address that Fizzy will send emails from. It should usually be an address from the same domain where you're running Fizzy. -- `env/clear/SMTP_ADDRESS`: The address of an SMTP server that you can send email through. You can use a 3rd-party service for this, like Sendgrid or Postmark, in which case their documentation will tell you what to use for this. -- `env/clear/MULTI_TENANT`: Set to `false` to enable single-tenant mode (disable multi-account signups). - - -Fizzy also requires a few environment variables to be set up, some of which contain secrets. -The simplest way to do this is to put them in a file called `.kamal/secrets`. -Because this file will contain secret credentials, it's important that you DON'T CHECK THIS FILE INTO YOUR REPO! You can add the filename to `.gitignore` to ensure you don't commit this file accidentally. - -If you use a password manager like 1Password, you can also opt to keep your secrets there instead. -Refer to the [Kamal documentation](https://kamal-deploy.org/docs/configuration/environment-variables/#secrets) for more information about how to do that. - -To store your secrets, create the file `.kamal/secrets` and enter something like the following: - -```ini -SECRET_KEY_BASE=12345 -VAPID_PUBLIC_KEY=something -VAPID_PRIVATE_KEY=somethingelse -SMTP_USERNAME=email-provider-username -SMTP_PASSWORD=email-provider-password -``` - -The values you enter here will be specific to you, and you can get or create them as follows: - -- `SECRET_KEY_BASE` should be a long, random secret. You can run `bin/rails secret` to create a suitable value for this. -- `SMTP_USERNAME` & `SMTP_PASSWORD` should be valid credentials for your SMTP server. If you're using a 3rd-party service here, consult their documentation for what to use. -- `VAPID_PUBLIC_KEY` & `VAPID_PRIVATE_KEY` are a pair of credentials that are used for sending notifications. You can create your own keys by starting a development console with: - - ```sh - bin/rails c - ``` - - And then run the following to create a new pair of keys: - - ```ruby - vapid_key = WebPush.generate_key - - puts "VAPID_PRIVATE_KEY=#{vapid_key.private_key}" - puts "VAPID_PUBLIC_KEY=#{vapid_key.public_key}" - ``` - -Once you've made all those changes, commit them to your fork so they're saved. - -### Deploy Fizzy! - -You can now do your first deploy by running: - -```sh -bin/kamal setup -``` - -This will set up Docker (if needed), build your Fizzy app container, configure it, and start it running. - -After the first deploy is done, any subsequent steps won't need to do that initial setup. So for future deploys you can just run: - -```sh -bin/kamal deploy -``` - -## File storage (Active Storage) - -Production uses the local disk service by default. To use any other service defined in `config/storage.yml`, set `ACTIVE_STORAGE_SERVICE`. - -To use the included `s3` service, set: - -- `ACTIVE_STORAGE_SERVICE=s3` -- `S3_ACCESS_KEY_ID` -- `S3_BUCKET` (defaults to `fizzy-#{Rails.env}-activestorage`) -- `S3_REGION` (defaults to `us-east-1`) -- `S3_SECRET_ACCESS_KEY` - -Optional for S3-compatible endpoints: - -- `S3_ENDPOINT` -- `S3_FORCE_PATH_STYLE=true` -- `S3_REQUEST_CHECKSUM_CALCULATION` (defaults to `when_supported`) -- `S3_RESPONSE_CHECKSUM_VALIDATION` (defaults to `when_supported`) ## Development -### Setting up +You are welcome -- and encouraged -- to modify Fizzy to your liking. +Please see our [Development guide](docs/development.md) for how to get Fizzy set up for local development. -First, get everything installed and configured with: - -```sh -bin/setup -bin/setup --reset # Reset the database and seed it -``` - -And then run the development server: - -```sh -bin/dev -``` - -You'll be able to access the app in development at http://fizzy.localhost:3006. - -To login, enter `david@example.com` and grab the verification code from the browser console to sign in. - -### Web Push Notifications - -Fizzy uses VAPID (Voluntary Application Server Identification) keys to send browser push notifications. For notifications to work in development you'll need to generate a key pair and set these environment variables: - -- `VAPID_PRIVATE_KEY` -- `VAPID_PUBLIC_KEY` - -Generate them with the `web-push` gem: - -```ruby -vapid_key = WebPush.generate_key - -puts "VAPID_PRIVATE_KEY=#{vapid_key.private_key}" -puts "VAPID_PUBLIC_KEY=#{vapid_key.public_key}" -``` - -### Running tests - -For fast feedback loops, unit tests can be run with: - -```sh -bin/rails test -``` - -The full continuous integration tests can be run with: - -```sh -bin/ci -``` - -### Database configuration - -Fizzy works with SQLite by default and supports MySQL too. You can switch adapters with the `DATABASE_ADAPTER` environment variable. For example, to develop locally against MySQL: - -```sh -DATABASE_ADAPTER=mysql bin/setup --reset -DATABASE_ADAPTER=mysql bin/ci -``` - -The remote CI pipeline will run tests against both SQLite and MySQL. - -### Outbound Emails - -You can view email previews at http://fizzy.localhost:3006/rails/mailers. - -You can enable or disable [`letter_opener`](https://github.com/ryanb/letter_opener) to open sent emails automatically with: - -```sh -bin/rails dev:email -``` - -Under the hood, this will create or remove `tmp/email-dev.txt`. - -## SaaS gem - -37signals bundles Fizzy with [`fizzy-saas`](https://github.com/basecamp/fizzy-saas), a companion gem that links Fizzy with our billing system and contains our production setup. - -This gem depends on some private git repositories and it is not meant to be used by third parties. But we hope it can serve as inspiration for anyone wanting to run fizzy on their own infrastructure. ## Contributing We welcome contributions! Please read our [style guide](STYLE.md) before submitting code. + ## License Fizzy is released under the [O'Saasy License](LICENSE.md). diff --git a/config/deploy.yml b/config/deploy.yml index 73c9f57ae..5550c317e 100644 --- a/config/deploy.yml +++ b/config/deploy.yml @@ -30,7 +30,7 @@ env: clear: MAILER_FROM_ADDRESS: support@example.com # The email "from" address that Fizzy sends email from SMTP_ADDRESS: mail.example.com # The SMTP server you'll use to send email - MULTI_TENANT: false # Set to false to enable single-tenant mode + MULTI_TENANT: false # Set to true to allow multiple accounts to sign up SOLID_QUEUE_IN_PUMA: true # Run background jobs in the app container diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 000000000..e253e430b --- /dev/null +++ b/docs/development.md @@ -0,0 +1,80 @@ +## Development + +### Setting up + +First, get everything installed and configured with: + +```sh +bin/setup +bin/setup --reset # Reset the database and seed it +``` + +And then run the development server: + +```sh +bin/dev +``` + +You'll be able to access the app in development at http://fizzy.localhost:3006. + +To login, enter `david@example.com` and grab the verification code from the browser console to sign in. + +### Web Push Notifications + +Fizzy uses VAPID (Voluntary Application Server Identification) keys to send browser push notifications. For notifications to work in development you'll need to generate a key pair and set these environment variables: + +- `VAPID_PRIVATE_KEY` +- `VAPID_PUBLIC_KEY` + +Generate them with the `web-push` gem: + +```ruby +vapid_key = WebPush.generate_key + +puts "VAPID_PRIVATE_KEY=#{vapid_key.private_key}" +puts "VAPID_PUBLIC_KEY=#{vapid_key.public_key}" +``` + +### Running tests + +For fast feedback loops, unit tests can be run with: + +```sh +bin/rails test +``` + +The full continuous integration tests can be run with: + +```sh +bin/ci +``` + +### Database configuration + +Fizzy works with SQLite by default and supports MySQL too. You can switch adapters with the `DATABASE_ADAPTER` environment variable. For example, to develop locally against MySQL: + +```sh +DATABASE_ADAPTER=mysql bin/setup --reset +DATABASE_ADAPTER=mysql bin/ci +``` + +The remote CI pipeline will run tests against both SQLite and MySQL. + +### Outbound Emails + +You can view email previews at http://fizzy.localhost:3006/rails/mailers. + +You can enable or disable [`letter_opener`](https://github.com/ryanb/letter_opener) to open sent emails automatically with: + +```sh +bin/rails dev:email +``` + +Under the hood, this will create or remove `tmp/email-dev.txt`. + +## SaaS gem + +37signals bundles Fizzy with [`fizzy-saas`](https://github.com/basecamp/fizzy-saas), a companion gem that links Fizzy with our billing system and contains our production setup. + +This gem depends on some private git repositories and it is not meant to be used by third parties. But we hope it can serve as inspiration for anyone wanting to run fizzy on their own infrastructure. + diff --git a/docs/docker-deployment.md b/docs/docker-deployment.md new file mode 100644 index 000000000..15ec58367 --- /dev/null +++ b/docs/docker-deployment.md @@ -0,0 +1,166 @@ +## Deploying with Docker + +We provide pre-built Docker images that can be used to run Fizzy on your own server. + +If you don't need to change the source code, and just want the out-of-the-box Fizzy experience, this can be a great way to get started. + +You'll find the latest version of Fizzy's Docker image at `ghcr.io/basecamp/fizzy:main`. +To run it you'll need three things: a machine that runs Docker; a mounted volume (so that your database is stored somewhere that is kept around between restarts); and some environment variables for configuration. + +### Mounting a storage volume + +The standard Fizzy setup keeps all of its storage inside the path `/rails/storage`. +By default Docker containers don't persist storage between runs, so you'll want to mount a persistent volume into that location. + +The simplest way to do this is with the `--volume` flag with `docker run`. For example: + +```sh +docker run --volume fizzy:/rails/storage ghcr.io/basecamp/fizzy:main +``` + +That will create a named volume (called `fizzy`) and mount it into the correct path. +Docker will manage where that volume is actually stored on your server. + +You can also specify the data location yourself, mount a network drive, and more. +Check the Docker documentation to find out more about what's available. + +### Configuring with environment variables + +To configure your Fizzy installation, you can use environment variables. +Fizzy has several of them. +Many of these are optional, but at a minimum you'll want to configure your secret key, your SSL domain, and your SMTP email settings. + +#### Secret Key Base + +Various features inside Fizzy rely on cryptography to work (such as secure links). +To set this up, you need to provide a secret value that will be used as the basis of those secrets. +This value can be anything, but it should be unguessable, and specific to your instance. + +You can use any long random string for this, or you can have the Fizzy codebase generate one for you by running: + +```sh +bin/rails secret +``` + +Once you have one, set it in the `SECRET_KEY_BASE` environment variable: + +```sh +docker run --environment SECRET_KEY_BASE=abcdefabcdef ... +``` + +#### SSL + +If you want the Fizzy container to handle its own SSL automatically, you just need to specify the domain name that you're running it on. +You can do that with the `TLS_DOMAIN` environment variable. +Note that if you're using SSL, you'll want to allow traffic on ports 80 and 443. +So if you were running on `fizzy.example.com` you could enable SSL like this: + +```sh +docker run --publish 80:80 --publish 443:443 --environment TLS_DOMAIN=fizzy.example.com ... +``` + +If you are terminating SSL in some other proxy in front of Fizzy, then you don't need to set `TLS_DOMAIN`, and can just publish port 80: +```sh +docker run --publish 80:80 ... +``` + +If you aren't using SSL at all (for example, if you want to run it locally on your laptop) then you should specify `DISABLE_SSL=true` instead: + +```sh +docker run --publish 80:80 --environment DISABLE_SSL=true ... +``` + +#### SMTP Email + +Fizzy needs to be able to send email for its sign up/sign in flow, and for its regular summary emails. +The easiest way to set this up is to use a 3rd-party email provider (such as Postmark, Sendgrid, and so on). +You can then plug all your SMTP settings from that provider into Fizzy via the following environment variables: + +- `MAILER_FROM_ADDRESS` - the "from" address that Fizzy should use to send email +- `SMTP_ADDRESS` - the address of the SMTP server you'll send through +- `SMTP_PORT` - the port number you use to connect to that SMTP server (the default is 587) +- `SMTP_USERNAME`/`SMTP_PASSWORD` - the credentials for logging in to the SMTP server + +Less commonly, you might also need to set some of the following: + +- `SMTP_DOMAIN` - the domain name advertised to the server when connecting +- `SMTP_AUTHENTICATION` - if you need an authentication method other than the default `plain` +- `SMTP_ENABLE_STARTTLS_AUTO` - if you need to disable TLS on the SMTP connection + +You can find out more about all these settings in the [Rails Action Mailer documentation](https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration). + +#### VAPID keys + +Fizzy can also send Web Push notifications. +To do this it needs a VAPID key pair. + +You can create your own keys by starting a development console with: + +```sh +bin/rails c +``` + +And then run the following to create the keypair: + +```ruby +vapid_key = WebPush.generate_key + +puts "VAPID_PRIVATE_KEY=#{vapid_key.private_key}" +puts "VAPID_PUBLIC_KEY=#{vapid_key.public_key}" +``` + +Set those in the `VAPID_PRIVATE_KEY` and `VAPID_PUBLIC_KEY` environment variables. + +#### S3 storage (optional) + +If you're rather that uploaded files were stored in an S3 bucket, rather than your mounted volume, you can set that up. + +First set `ACTIVE_STORAGE_SERVICE` to `s3`. +Then set the following as appropriate for your S3 bucket: + +- `S3_BUCKET` +- `S3_REGION` +- `S3_ACCESS_KEY_ID` +- `S3_SECRET_ACCESS_KEY` + +If you're using a provider other than AWS, you will also need some of the following: + +- `S3_ENDPOINT` +- `S3_FORCE_PATH_STYLE` +- `S3_REQUEST_CHECKSUM_CALCULATION` +- `S3_RESPONSE_CHECKSUM_VALIDATION` + +#### Multi-tenant mode + +By default, when you run the Fizzy Docker image you'll be limited to creating a single account (although that account can have as many users as you like). +This is for convenience: typically when you self-host you'll be running a single account, so in this mode new account signups are automatically disabled as soon as you've created your first account. + +If you do want to allow multiple accounts to be created in your instance, set `MULTI_TENANT=true` + +## Example + +Here's an example of a `docker-compose.yml` that you could use to run Fizzy via `docker compose up` + +```yaml +services: + web: + image: ghcr.io/basecamp/fizzy:main + restart: unless-stopped + ports: + - "80:80" + - "443:443" + environment: + - SECRET_KEY_BASE=abcdefabcdef + - TLS_DOMAIN=fizzy.example.com + - MAILER_FROM_ADDRESS=fizzy@example.com + - SMTP_ADDRESS=mail.example.com + - SMTP_USERNAME=user + - SMTP_PASSWORD=pass + - VAPID_PRIVATE_KEY=myvapidprivatekey + - VAPID_PUBLIC_KEY=myvapidpublickey + volumes: + - fizzy:/rails/storage + +volumes: + fizzy: +``` diff --git a/docs/kamal-deployment.md b/docs/kamal-deployment.md new file mode 100644 index 000000000..5da8f37f0 --- /dev/null +++ b/docs/kamal-deployment.md @@ -0,0 +1,111 @@ +## Deploying Fizzy with Kamal + +If you'd like to run Fizzy on your own server while having the freedom to easily make changes to its code, we recommend deploying it with [Kamal](https://kamal-deploy.org/). +Kamal makes it easy to set up a bare server, copy the application to it, and manage the configuration settings that it uses. + +(Kamal is also what we use to deploy Fizzy at 37signals. If you're curious about what our deployment configuration looks like, you can find it inside [`fizzy-saas`](https://github.com/basecamp/fizzy-saas).) + +This repo contains a starter deployment file that you can modify for your own specific use. That file lives at [config/deploy.yml](config/deploy.yml), which is the default place where Kamal will look for it. + +The steps to configure your very own Fizzy are: + +1. Fork the repo +2. Edit few things in config/deploy.yml and .kamal/secrets +3. Run `kamal setup` to do your first deploy. + +We'll go through each of these in turn. + +### Fork the repo + +To make it easy to customise Fizzy's settings for your own instance, you should start by creating your own GitHub fork of the repo. +That allows you to commit your changes, and track them over time. +You can always re-sync your fork to pick up new changes from the main repo over time. + +Once you've got your fork ready, run `bin/setup` from within it, to make sure everything is installed. + +### Editing the configuration + +The config/deploy.yml has been mostly set up for you, but you'll need to fill out some sections that are specific to your instance. +To get started, the parts you need to change are all in the "About your deployment" section. +We've added comments to that file to highlight what each setting needs to be, but the main ones are: + +- `servers/web`: Enter the hostname of the server you're deploying to here. This should be an address that you can access via `ssh`. +- `ssh/user`: If you access your server a `root` you can leave this alone; if you use a different user, set it here. +- `proxy/ssl` and `proxy/host`: Kamal can set up SSL certificates for you automatically. To enable that, set the hostname again as `host`. If you don't want SSL for some reason, you can set `ssl: false` to turn it off. +- `env/clear/MAILER_FROM_ADDRESS`: This is the email address that Fizzy will send emails from. It should usually be an address from the same domain where you're running Fizzy. +- `env/clear/SMTP_ADDRESS`: The address of an SMTP server that you can send email through. You can use a 3rd-party service for this, like Sendgrid or Postmark, in which case their documentation will tell you what to use for this. +- `env/clear/MULTI_TENANT`: Set to `true` if you want to allow multiple accounts to sign up on your server (by default, Fizzy will allow you to create a single account). + +Fizzy also requires a few environment variables to be set up, some of which contain secrets. +The simplest way to do this is to put them in a file called `.kamal/secrets`. +Because this file will contain secret credentials, it's important that you DON'T CHECK THIS FILE INTO YOUR REPO! You can add the filename to `.gitignore` to ensure you don't commit this file accidentally. + +If you use a password manager like 1Password, you can also opt to keep your secrets there instead. +Refer to the [Kamal documentation](https://kamal-deploy.org/docs/configuration/environment-variables/#secrets) for more information about how to do that. + +To store your secrets, create the file `.kamal/secrets` and enter something like the following: + +```ini +SECRET_KEY_BASE=12345 +VAPID_PUBLIC_KEY=something +VAPID_PRIVATE_KEY=somethingelse +SMTP_USERNAME=email-provider-username +SMTP_PASSWORD=email-provider-password +``` + +The values you enter here will be specific to you, and you can get or create them as follows: + +- `SECRET_KEY_BASE` should be a long, random secret. You can run `bin/rails secret` to create a suitable value for this. +- `SMTP_USERNAME` & `SMTP_PASSWORD` should be valid credentials for your SMTP server. If you're using a 3rd-party service here, consult their documentation for what to use. +- `VAPID_PUBLIC_KEY` & `VAPID_PRIVATE_KEY` are a pair of credentials that are used for sending notifications. You can create your own keys by starting a development console with: + + ```sh + bin/rails c + ``` + + And then run the following to create a new pair of keys: + + ```ruby + vapid_key = WebPush.generate_key + + puts "VAPID_PRIVATE_KEY=#{vapid_key.private_key}" + puts "VAPID_PUBLIC_KEY=#{vapid_key.public_key}" + ``` + +Once you've made all those changes, commit them to your fork so they're saved. + +### Deploy Fizzy! + +You can now do your first deploy by running: + +```sh +bin/kamal setup +``` + +This will set up Docker (if needed), build your Fizzy app container, configure it, and start it running. + +After the first deploy is done, any subsequent steps won't need to do that initial setup. So for future deploys you can just run: + +```sh +bin/kamal deploy +``` + +### Configuring file storage (Active Storage) + +Production uses the local disk service by default. To use any other service defined in `config/storage.yml`, set `ACTIVE_STORAGE_SERVICE`. + +To use the included `s3` service, set: + +- `ACTIVE_STORAGE_SERVICE=s3` +- `S3_ACCESS_KEY_ID` +- `S3_BUCKET` (defaults to `fizzy-#{Rails.env}-activestorage`) +- `S3_REGION` (defaults to `us-east-1`) +- `S3_SECRET_ACCESS_KEY` + +Optional for S3-compatible endpoints: + +- `S3_ENDPOINT` +- `S3_FORCE_PATH_STYLE=true` +- `S3_REQUEST_CHECKSUM_CALCULATION` (defaults to `when_supported`) +- `S3_RESPONSE_CHECKSUM_VALIDATION` (defaults to `when_supported`) +