Skip to main content
Deploy Mill on a server already registered with your Towbar installation. You need a Git repository for the deployment files and separate domains for Mill and its API.

1. Add the deployment files

Copy these files into your deployment repository. Keep their folder structure:
towbar.yml
.towbar
services
mill-api.service.yml
mill-web.service.yml
datastores
mill-postgres.datastore.yml
Replace 192.0.2.10 with your registered server’s address and replace the two example.com domains with your own. Both domains must use the same site, such as tasks.example.com and tasks-api.example.com, for browser sessions to work. The examples use direct TLS with unproxied DNS records. Adjust the TLS settings if your Towbar installation uses another certificate method. Resource limits are starting values; check available server capacity before deployment.
The API and UI references above are the published Mill 1.0.6 images. For another release, copy both digest-pinned image references from that release and update them together. Towbar selects the server’s native AMD64 or ARM64 image.

2. Connect the repository

Add your deployment repository as a source in Towbar, map production to your deployment branch, and sync it. Review the two services and PostgreSQL datastore. The files set autoDeploy: false, so syncing does not start workloads. Save runtime values before starting each deployment.

3. Add runtime values

Run this command twice to generate separate values for the database password and MILL_SECRET:
Save these values in Towbar’s runtime secrets, rather than in Git: Use postgres://mill:YOUR_DATABASE_PASSWORD@mill-postgres:5432/mill as DATABASE_URL. Replace the placeholder privately with the datastore’s password. Percent-encode special characters if you use a password format other than the generated hexadecimal value. Set MILL_WEB_URL=https://tasks.example.com and MILL_API_URL=https://tasks-api.example.com, replacing the domains with your final HTTPS origins. The API and UI must use the same API origin. Keep MILL_SECRET private and preserve it across upgrades and restores.

Optional email and attachments

Leave the commented runtime keys out for an installation without email or attachments. Towbar requires a value for every uncommented runtime key. For identity and invitation email, uncomment the MILL_SMTP_* keys you need on the API and supply their values. MILL_SMTP_HOST and MILL_SMTP_FROM are required together; configure user and password together when authentication is needed. See email configuration for ports and TLS settings. For task attachments, uncomment all five MILL_S3_* keys on the API and MILL_S3_ENDPOINT on the UI. Use a private bucket and a browser-accessible HTTPS endpoint with CORS allowing your exact MILL_WEB_URL. The UI needs only the public endpoint; storage credentials stay on the API. Follow attachment storage configuration for credentials, CORS and lifecycle rules.

4. Deploy Mill

Deploy PostgreSQL first, then API and UI. Wait for each workload to become ready before continuing. Towbar manages the datastore’s persistent volume; retain it during upgrades. The API prepares the database automatically before accepting requests. The UI serves the app and its public runtime configuration. Browser requests go directly to the public API origin, so both API and UI domains must resolve and have valid HTTPS certificates. Keep PostgreSQL on the private mill network. Check /health/ready on both public origins, then open the app. A ready UI alone does not establish database or API connectivity.

5. Create your team

Open the API startup log in Towbar and find the line labelled Mill installation setup secret. Open your Mill URL, enter the setup secret, and create your team and first administrator. The setup secret is separate from MILL_SECRET; keep it and the startup logs private. There is no default account. Setup closes once the workspace exists. Verify sign-in and task creation, then continue with your first board and task.

Use an existing database

Omit the PostgreSQL datastore file and point DATABASE_URL at a dedicated PostgreSQL database reachable from the API container. Include the provider’s required TLS settings and use a database user that can create and modify Mill’s tables. The API and UI manifests otherwise stay the same.

Backups and upgrades

Set up Backup and recovery before storing production data. Preserve the original MILL_SECRET, keep encrypted backups off the deployment server, and rehearse a restore into a separate database. Back up attachment objects separately when S3 storage is enabled. The datastore example does not configure a Towbar backup destination; configure one separately if needed. Before upgrading, back up the database and read Upgrades. Update the API and UI digest pair together, sync the repository, and deploy API followed by UI. The examples use recreate rollouts with maintenance mode, so expect brief downtime. An image rollback does not undo a database migration.