This project uses Thruster, a zero-config HTTP/2 proxy from Basecamp, to enhance application performance and simplify deployment. Thruster sits in front of the Rails/Puma server and provides HTTP/2 support, asset caching, compression, and TLS termination.
Thruster is a small, fast HTTP/2 proxy designed specifically for Ruby web applications. It provides:
- HTTP/2 Support: Automatic HTTP/2 with multiplexing for faster asset loading
- Asset Caching: X-Sendfile support and intelligent caching for static assets
- Compression: Automatic gzip/Brotli compression for responses
- TLS Termination: Built-in Let's Encrypt support for production deployments
- Zero Configuration: Works out of the box with sensible defaults
Previously, this project used Puma's --early-hints flag to send HTTP/2 server push hints. Thruster provides several advantages:
- Simpler Configuration: No need to configure early hints in your application code
- Better HTTP/2 Support: Full HTTP/2 implementation, not just early hints
- Asset Optimization: Built-in caching and compression without additional configuration
- Production Ready: TLS termination and Let's Encrypt integration for production
- Faster Asset Delivery: More efficient handling of static assets
Thruster is already installed in this project via the Gemfile:
gem "thruster"After running bundle install, the thrust executable is available.
All Procfiles in this project have been updated to use Thruster:
web: bundle exec thrust bin/rails server
rails: bundle exec thrust bin/rails server -p 3000
web: bundle exec thrust bin/rails server -p 3001
web: bundle exec thrust bin/rails server -p 3000
web: bundle exec thrust bin/rails server -p 3000
Thruster uses sensible defaults:
- Port: Listens on port specified by Rails server (or PORT env var)
- Cache: Automatically caches static assets from
public/ - Compression: Enables gzip/Brotli compression automatically
- HTTP/2: Enabled by default when using HTTPS
You can customize Thruster behavior using environment variables:
# Set custom cache directory
THRUSTER_CACHE_DIR=/path/to/cache
# Adjust cache size (default: 64MB)
THRUSTER_CACHE_SIZE=128M
# Set custom TLS certificate (production)
THRUSTER_TLS_CERT=/path/to/cert.pem
THRUSTER_TLS_KEY=/path/to/key.pem
# Enable debug logging
THRUSTER_DEBUG=1For most use cases, the defaults work perfectly without any additional configuration.
Use any of the existing Procfile commands:
# Development with Hot Module Replacement
foreman start -f Procfile.dev
# Development with static assets
foreman start -f Procfile.dev-static
# Production-like assets in development
foreman start -f Procfile.dev-prod-assetsThruster will automatically:
- Start a proxy server on the configured port
- Forward requests to Rails/Puma
- Cache and compress assets
- Serve static files efficiently
When the server starts, you'll see Thruster initialization in the logs:
[thrust] Starting Thruster HTTP/2 proxy
[thrust] Proxying to http://localhost:3000
[thrust] Serving from ./public
Thruster works seamlessly with Heroku. The standard Procfile is already configured:
web: bundle exec thrust bin/rails server
Heroku automatically:
- Provides TLS termination at the router level
- Sets the PORT environment variable
- Manages process scaling
For Control Plane deployments, Thruster requires specific configuration in two places:
The Dockerfile CMD must use Thruster (.controlplane/Dockerfile):
CMD ["bundle", "exec", "thrust", "bin/rails", "server"]The workload port should remain as HTTP/1.1 (.controlplane/templates/rails.yml):
ports:
- number: 3000
protocol: http # Keep as http, NOT http2Important: Keep the protocol as http (not http2) because:
- Thruster handles HTTP/2 on the public-facing TLS connection
- Control Plane's load balancer communicates with containers via HTTP/1.1
- Setting
protocol: http2causes 502 protocol errors
Note: On Control Plane/Kubernetes, the Dockerfile CMD determines container startup, NOT the Procfile. This differs from Heroku where Procfile is used.
# Apply the updated workload template
cpflow apply-template rails -a <app-name>
# Build and deploy new image
cpflow build-image -a <app-name>
cpflow deploy-image -a <app-name>
# Verify Thruster is running
cpflow logs -a <app-name>For detailed Control Plane setup, see .controlplane/readme.md.
For VPS or bare-metal deployments, Thruster can handle TLS termination with Let's Encrypt:
# Set your domain for automatic Let's Encrypt certificates
THRUSTER_DOMAIN=yourdomain.com bundle exec thrust bin/rails serverThruster will automatically:
- Obtain SSL certificates from Let's Encrypt
- Handle certificate renewal
- Serve your app over HTTPS with HTTP/2
Thruster logs important events:
[thrust] Starting Thruster HTTP/2 proxy
[thrust] Proxying to http://localhost:3000
[thrust] Serving from ./public
[thrust] Cache hit: /packs/application-abc123.js
[thrust] Compressed response: 1.2MB -> 250KB
Enable verbose logging:
THRUSTER_DEBUG=1 foreman start -f Procfile.devThis shows:
- All proxied requests
- Cache hit/miss information
- Compression ratios
- HTTP/2 connection details
Monitor Thruster's impact:
- Asset Load Times: Check browser DevTools Network tab for HTTP/2 multiplexing
- Cache Efficiency: Look for
X-Cache: HITheaders in responses - Compression: Check
Content-Encoding: brorgzipheaders - Response Times: Should see faster initial page loads
Issue: Thruster fails to start Solution: Check if another process is using the port:
lsof -ti:3000 | xargs kill -9Issue: Static assets aren't being cached
Solution: Ensure assets are in the public/ directory and have proper cache headers:
# config/environments/production.rb
config.public_file_server.enabled = true
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=31536000'
}Issue: Browser shows HTTP/1.1 connections Solution: HTTP/2 requires HTTPS. In development, use a tool like mkcert or test in production with proper TLS.
Previous configuration:
web: bundle exec puma -C config/puma.rb --early-hints
New configuration:
web: bundle exec thrust bin/rails server
Changes:
- Removed
--early-hintsflag from all Procfiles - No changes needed to application code
- Better performance with full HTTP/2 support
Thruster works seamlessly with Shakapacker for both Webpack and Rspack:
- Compiled assets in
public/packs/are automatically cached - Manifest files are properly served
- Hot Module Replacement (HMR) still works in development
To enable early hints in production, add the following to your config/shakapacker.yml:
# config/shakapacker.yml
production:
<<: *default
# ... other settings ...
# Early hints configuration
early_hints:
enabled: true
debug: true # Set to true to output debug info as HTML commentsThis allows Shakapacker to send Link headers for webpack assets, which Thruster can use to optimize asset loading via HTTP/2 server push or 103 Early Hints responses.
Based on typical Rails applications with Thruster:
- Initial Page Load: 20-30% faster due to HTTP/2 multiplexing
- Asset Delivery: 40-60% reduction in transfer size with Brotli compression
- Cache Hit Rate: 80-95% for static assets after warmup
- Server Load: Reduced by 30-40% due to efficient asset serving
- Thruster GitHub Repository
- HTTP/2 Explained
- Deploying Rails 8 with Thruster
- Kamal Handbook - Thruster Section
For issues related to:
- Thruster: GitHub Issues
- This Project: Forum or GitHub Issues
- React on Rails: Slack Channel