Deploying Foodsoft
Introduction
Foodsoft is a web-based open-source software solution designed to manage non-profit food cooperatives. Built with Ruby on Rails, this powerful platform streamlines the entire food co-op workflow, from product catalog management and member ordering to accounting, job scheduling, and supplier coordination. Foodsoft empowers community-run food cooperatives to operate efficiently while maintaining their democratic, member-owned values.
Foodsoft is known for:
- Complete Product Catalog: Manage products from multiple suppliers with detailed specifications, pricing, and availability
- Member Ordering: User-friendly online ordering system for cooperative members with real-time stock tracking
- Cooperative Accounting: Built-in financial management, member accounts, transaction tracking, and invoice generation
- Job Scheduling: Coordinate work shifts and volunteer tasks required to operate the cooperative
- Supplier Management: Track relationships with producers, manage order cycles, and maintain supplier catalogs
- Order Groups: Support for household ordering groups sharing deliveries and responsibilities
- Flexible Pricing: Support for various pricing models including markup percentages and member discounts
- Message System: Internal messaging and notification system for member communication
- Multi-Language: Available in German, English, French, Dutch, and more languages
- Open API: RESTful API for integrations and custom extensions
- Plugin Architecture: Extensible system with plugins for payments, wikis, documents, and more
Common use cases include food cooperatives, buying clubs, community-supported agriculture (CSA) programs, collective purchasing groups, and local food networks operating on a non-profit basis.
This comprehensive guide walks you through deploying Foodsoft on Klutch.sh using Docker, including PostgreSQL database configuration, Redis setup, persistent storage, environment variables, and production-ready best practices.
Why Deploy Foodsoft on Klutch.sh?
- Simplified Infrastructure: No need to manage servers, databases, or complex Rails configurations manually
- Persistent Storage: Reliable volume storage for uploaded documents, images, and attachments
- PostgreSQL Integration: Easy database setup with persistent data storage for cooperative data
- Redis Support: Built-in support for background job processing and caching
- Automatic HTTPS: Secure connections out of the box for safe member transactions
- Environment Variables: Secure configuration management without hardcoding credentials
- Easy Scaling: Scale resources as your cooperative membership grows
- Cost-Effective: Pay only for what you use with transparent pricing
- Zero-Downtime Deployments: Rolling updates without disrupting member access
Prerequisites
Before you begin, ensure you have the following:
- A Klutch.sh account
- A GitHub account with a repository for your Foodsoft project
- Docker installed locally for testing (optional but recommended)
- Basic understanding of Docker, Ruby on Rails applications, PostgreSQL, and Redis
- A PostgreSQL database (either external or deployed on Klutch.sh - see our PostgreSQL deployment guide)
- A Redis instance for background jobs and caching (see our Redis deployment guide)
Installation and Setup
Step 1: Create Your Project Directory
First, create a new directory for your Foodsoft deployment project:
mkdir foodsoft-klutchcd foodsoft-klutchgit initStep 2: Create the Dockerfile
Create a Dockerfile in your project root directory. This will define your Foodsoft container configuration using the official Docker image:
FROM ghcr.io/foodcoops/foodsoft:latest
# Foodsoft runs on Puma web server with Rails 7.x# The official image includes Ruby 3.x and all required gemsEXPOSE 3000
# The base image includes:# - Ruby 3.2+ with bundled gems# - Rails 7.x framework# - Node.js for asset compilation# - All Foodsoft application code and dependencies
# Persistent volumes should be mounted at:# /usr/src/app/tmp - for temporary files, cache, and PID files# /usr/src/app/public/system - for uploaded attachments and images# /usr/src/app/log - for application logs (optional)
# Environment variables for configuration# These should be configured in the Klutch.sh dashboard:# - DATABASE_URL: PostgreSQL connection string# - REDIS_URL: Redis connection string for Sidekiq background jobs# - SECRET_KEY_BASE: Rails secret key for sessions (generate with: rails secret)# - SMTP settings for email notifications# - Multi-coop settings if hosting multiple cooperatives
# Health check endpointHEALTHCHECK --interval=30s --timeout=3s --start-period=40s \ CMD curl -f http://localhost:3000/health || exit 1Note: The official Foodsoft Docker image is production-ready and includes all necessary dependencies.
Step 3: Create Database Configuration
Create a .env.example file to document required environment variables:
# Database ConfigurationDATABASE_URL=postgresql://username:password@postgres-host:5432/foodsoft_production
# Redis Configuration for Background JobsREDIS_URL=redis://redis-host:6379/0
# Rails Secret Key Base (generate with: rails secret)SECRET_KEY_BASE=your_secret_key_here_generate_a_long_random_string
# Application SettingsRAILS_ENV=productionRAILS_LOG_TO_STDOUT=trueRAILS_SERVE_STATIC_FILES=true
# Multi-Coop Settings (if hosting multiple cooperatives)FOODSOFT_MULTI_COOP_INSTALL=false# If true, configure:# FOODSOFT_APP_CONFIG_PATH=/usr/src/app/config/app_config.yml
# SMTP Configuration for Email NotificationsSMTP_ADDRESS=smtp.example.comSMTP_PORT=587SMTP_DOMAIN=example.comSMTP_USER_NAME=notifications@example.comSMTP_PASSWORD=your_smtp_passwordSMTP_AUTHENTICATION=plainSMTP_ENABLE_STARTTLS_AUTO=true
# Email SenderSMTP_SENDER_ADDRESS=noreply@example.comSMTP_SENDER_NAME=Your Food Coop
# Application HostFOODSOFT_HOST=foodsoft.example.com
# Optional: Error Tracking# SENTRY_DSN=your_sentry_dsn_here
# Optional: Time ZoneTZ=Europe/BerlinStep 4: Create Application Configuration
For single-coop installations, create a config/app_config.yml file:
# config/app_config.yml - Foodsoft Application Configuration
# Default configuration for all environmentsdefault: &defaults # Basic Settings name: Your Food Cooperative
# Homepage URL homepage: https://foodsoft.example.com
# Email settings email_sender: noreply@example.com
# Currency currency_unit: € currency_space: true
# Order settings # Minimum order quantity minimum_balance: 0
# How many days before order ends to send reminder emails order_schedule: ends_before_days: 3
# Tax settings tax_default: 7.0
# Invoice settings price_markup: 10.0 # Default markup percentage
# Member settings # Require members to agree to privacy policy privacy_policy_url: https://foodsoft.example.com/privacy
# Allow members to pick up orders themselves use_self_service: false
# Financial settings minimum_order_quantity: 1
# Document settings # Upload limit in MB documents: max_size: 10
# Group order settings # Allow group orders (households) use_group_orders: true
# Wiki settings use_wiki: true
# Messages settings use_messages: true
# Workgroups/Tasks settings use_tasks: true
# Production environmentproduction: <<: *defaultsStep 5: Initialize Git Repository
Before deploying, commit your files to Git:
# Create .gitignorecat > .gitignore << EOF.env*.logtmp/*!tmp/.keeppublic/system/*!public/system/.keepEOF
# Add and commit filesgit add Dockerfile .env.example config/app_config.yml .gitignoregit commit -m "Initial Foodsoft setup for Klutch.sh deployment"Deploying to Klutch.sh
Step 1: Push to GitHub
- Create a new repository on GitHub.
- Push your local repository:
git remote add origin https://github.com/yourusername/foodsoft-klutch.gitgit branch -M mastergit push -u origin masterStep 2: Create a New App on Klutch.sh
- Log in to your Klutch.sh dashboard.
- Click New App and select your GitHub repository.
- Klutch.sh will automatically detect your
Dockerfileand prepare for deployment. - Select HTTP traffic since Foodsoft is a web application.
- Set the internal port to 3000 (Rails/Puma default port).
Step 3: Configure Environment Variables
In the Klutch.sh dashboard, add the following environment variables:
Required Variables:
# Database Connection (use your PostgreSQL instance)DATABASE_URL=postgresql://username:password@your-postgres-host.klutch.sh:8000/foodsoft_production
# Redis Connection (use your Redis instance)REDIS_URL=redis://your-redis-host.klutch.sh:8000/0
# Rails Secret Key (generate with: openssl rand -hex 64)SECRET_KEY_BASE=your_64_character_secret_key_here
# Rails EnvironmentRAILS_ENV=productionRAILS_LOG_TO_STDOUT=trueRAILS_SERVE_STATIC_FILES=trueSMTP Configuration:
SMTP_ADDRESS=smtp.gmail.comSMTP_PORT=587SMTP_DOMAIN=example.comSMTP_USER_NAME=your-email@gmail.comSMTP_PASSWORD=your-app-passwordSMTP_AUTHENTICATION=plainSMTP_ENABLE_STARTTLS_AUTO=trueSMTP_SENDER_ADDRESS=noreply@example.comSMTP_SENDER_NAME=Your Food CoopOptional Variables:
# Application HostFOODSOFT_HOST=your-app.klutch.sh
# Time ZoneTZ=America/New_York
# Multi-Coop (if hosting multiple cooperatives)FOODSOFT_MULTI_COOP_INSTALL=falseMark sensitive variables like SECRET_KEY_BASE, DATABASE_URL, and SMTP_PASSWORD as secret.
Step 4: Attach Persistent Volumes
Foodsoft requires persistent storage for uploaded files and temporary data:
- In the Klutch.sh dashboard, navigate to Volumes.
- Add the following volumes:
Volume 1: Temporary Files and Cache
- Mount Path:
/usr/src/app/tmp - Size: 2 GB
- Purpose: Rails cache, PID files, and temporary data
Volume 2: Uploaded Files and Attachments
- Mount Path:
/usr/src/app/public/system - Size: 10 GB
- Purpose: Member-uploaded documents, product images, and attachments
Volume 3: Application Logs (Optional)
- Mount Path:
/usr/src/app/log - Size: 1 GB
- Purpose: Rails application logs for debugging
Step 5: Deploy Your Application
- Click Deploy in the Klutch.sh dashboard.
- Klutch.sh will build your Docker image and deploy the application.
- Monitor the build logs for any errors.
- Once deployed, your Foodsoft instance will be accessible at
https://your-app.klutch.sh.
Step 6: Initialize the Database
After first deployment, you need to initialize the database with Foodsoft schema and seed data:
- Access the application container via Klutch.sh terminal or SSH.
- Run database migrations:
rails db:schema:loadrails db:seedNote: The seed data creates an initial admin user. Check the Foodsoft documentation for default credentials.
Step 7: Create Your First Admin User
- Access your deployed Foodsoft instance at
https://your-app.klutch.sh. - Use the default admin credentials from the seed data, or create a new admin user via Rails console:
# Access Rails console in the containerrails console
# Create admin userUser.create!( first_name: "Admin", last_name: "User", email: "admin@example.com", password: "SecurePassword123!", password_confirmation: "SecurePassword123!", admin: true)Configuration
Database Configuration
Foodsoft uses PostgreSQL as its primary database. Configure the connection via the DATABASE_URL environment variable:
DATABASE_URL=postgresql://username:password@host:port/database_nameFor Klutch.sh-hosted PostgreSQL:
DATABASE_URL=postgresql://myuser:mypassword@postgres-app.klutch.sh:8000/foodsoft_productionDatabase Performance Tips:
- Enable connection pooling with
?pool=25in the DATABASE_URL - Use PostgreSQL 15+ for better JSON handling
- Regular VACUUM and ANALYZE for query performance
Redis Configuration
Foodsoft uses Redis for background job processing via Sidekiq and optional caching:
REDIS_URL=redis://host:port/database_numberFor Klutch.sh-hosted Redis:
REDIS_URL=redis://redis-app.klutch.sh:8000/0Redis Use Cases in Foodsoft:
- Sidekiq Background Jobs: Email sending, order processing, data imports
- Action Cable: Real-time notifications (if enabled)
- Rails Cache: Session storage and fragment caching
Email Configuration
Configure SMTP settings for member notifications, order confirmations, and password resets:
SMTP_ADDRESS=smtp.gmail.comSMTP_PORT=587SMTP_DOMAIN=yourdomain.comSMTP_USER_NAME=notifications@yourdomain.comSMTP_PASSWORD=your_app_passwordSMTP_AUTHENTICATION=plainSMTP_ENABLE_STARTTLS_AUTO=trueSMTP_SENDER_ADDRESS=noreply@yourdomain.comSMTP_SENDER_NAME=Your Food CoopRecommended SMTP Providers:
- SendGrid: Reliable with good free tier
- Mailgun: Excellent deliverability for transactional emails
- Amazon SES: Cost-effective for high volume
- Gmail: Suitable for small cooperatives (250 emails/day limit)
Multi-Coop Configuration
Foodsoft supports hosting multiple cooperatives in one installation:
FOODSOFT_MULTI_COOP_INSTALL=trueFOODSOFT_APP_CONFIG_PATH=/usr/src/app/config/app_config.ymlCreate config/app_config.yml with multiple coop configurations:
# Multi-coop configurationuse_multi_coop: true
# Coop 1coop_one: name: First Food Coop database: adapter: postgresql database: foodsoft_coop1 # ... other database settings homepage: https://coop1.example.com
# Coop 2coop_two: name: Second Food Coop database: adapter: postgresql database: foodsoft_coop2 homepage: https://coop2.example.comBackground Jobs
Foodsoft uses Sidekiq for background job processing. The official Docker image includes a startup script that manages both the web server and Sidekiq worker.
Jobs handled by Sidekiq:
- Sending order confirmation emails
- Processing bulk data imports
- Generating financial reports
- Cleaning up expired sessions
- Scheduled order reminders
Monitoring Sidekiq:
Access the Sidekiq web UI at: https://your-app.klutch.sh/admin/sidekiq (requires admin login)
Sample Code: Foodsoft API Integration
Foodsoft provides a RESTful API for integrations. Here are examples in multiple languages:
Ruby - Fetch Current Orders
require 'net/http'require 'json'require 'uri'
# Configure API credentialsapi_url = 'https://your-app.klutch.sh/api/v1'api_token = 'your_api_token_here'
# Fetch current ordersuri = URI("#{api_url}/orders")request = Net::HTTP::Get.new(uri)request['Authorization'] = "Bearer #{api_token}"request['Content-Type'] = 'application/json'
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| http.request(request)end
if response.code == '200' orders = JSON.parse(response.body) puts "Current orders: #{orders.length}"
orders.each do |order| puts "Order ##{order['id']}: #{order['name']} (ends: #{order['ends']})" endelse puts "Error: #{response.code} - #{response.body}"endPython - Create New Article
import requestsimport json
# Configure API credentialsapi_url = 'https://your-app.klutch.sh/api/v1'api_token = 'your_api_token_here'
headers = { 'Authorization': f'Bearer {api_token}', 'Content-Type': 'application/json'}
# Create new articlearticle_data = { 'article': { 'name': 'Organic Apples', 'unit': 'kg', 'price': 3.50, 'tax': 7.0, 'deposit': 0.0, 'supplier_id': 1, 'article_category_id': 2, 'availability': True }}
response = requests.post( f'{api_url}/articles', headers=headers, json=article_data)
if response.status_code == 201: article = response.json() print(f"Article created: {article['name']} (ID: {article['id']})")else: print(f"Error: {response.status_code} - {response.text}")JavaScript - Fetch Member Account Balance
const axios = require('axios');
// Configure API credentialsconst apiUrl = 'https://your-app.klutch.sh/api/v1';const apiToken = 'your_api_token_here';
// Fetch member account balanceasync function getMemberBalance(memberId) { try { const response = await axios.get( `${apiUrl}/user/financial_overview`, { headers: { 'Authorization': `Bearer ${apiToken}`, 'Content-Type': 'application/json' } } );
const financial = response.data; console.log(`Account Balance: €${financial.account_balance}`); console.log(`Available Credit: €${financial.available_credit}`);
return financial; } catch (error) { console.error('Error fetching balance:', error.response?.data || error.message); throw error; }}
// UsagegetMemberBalance(1) .then(data => console.log('Success:', data)) .catch(err => console.error('Failed:', err));PHP - Export Order Data
<?php
// Configure API credentials$apiUrl = 'https://your-app.klutch.sh/api/v1';$apiToken = 'your_api_token_here';
// Fetch order detailsfunction getOrderDetails($orderId) { global $apiUrl, $apiToken;
$ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => "$apiUrl/orders/$orderId", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer $apiToken", "Content-Type: application/json" ] ]);
$response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
if ($httpCode === 200) { $order = json_decode($response, true); echo "Order: {$order['name']}\n"; echo "Total: €{$order['total']}\n"; echo "Articles: " . count($order['order_articles']) . "\n";
// Export to CSV $filename = "order_{$orderId}_" . date('Y-m-d') . ".csv"; $fp = fopen($filename, 'w'); fputcsv($fp, ['Article', 'Quantity', 'Unit', 'Price', 'Total']);
foreach ($order['order_articles'] as $article) { fputcsv($fp, [ $article['name'], $article['quantity'], $article['unit'], $article['price'], $article['total'] ]); }
fclose($fp); echo "Exported to: $filename\n";
return $order; } else { echo "Error: $httpCode - $response\n"; return null; }}
// UsagegetOrderDetails(123);?>Local Development
For local development with all dependencies, create a docker-compose.yml file:
version: '3.8'
services: postgres: image: postgres:15-alpine environment: POSTGRES_DB: foodsoft_development POSTGRES_USER: foodsoft POSTGRES_PASSWORD: foodsoft ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data
redis: image: redis:7-alpine ports: - "6379:6379" volumes: - redis_data:/data
foodsoft: build: . ports: - "3000:3000" environment: DATABASE_URL: postgresql://foodsoft:foodsoft@postgres:5432/foodsoft_development REDIS_URL: redis://redis:6379/0 SECRET_KEY_BASE: development_secret_key_not_for_production RAILS_ENV: development volumes: - ./tmp:/usr/src/app/tmp - ./public/system:/usr/src/app/public/system - ./log:/usr/src/app/log depends_on: - postgres - redis command: bash -c "rails db:create db:migrate db:seed && rails server -b 0.0.0.0"
volumes: postgres_data: redis_data:Start the development environment:
docker-compose up -dAccess Foodsoft at http://localhost:3000.
Production Best Practices
Security Hardening
- Strong Secret Key: Generate a secure
SECRET_KEY_BASE:
openssl rand -hex 64- Database Credentials: Use strong passwords and limit database access to the application IP.
- HTTPS Only: Ensure all traffic uses HTTPS (Klutch.sh provides this automatically).
- Rate Limiting: Configure Rack::Attack in Rails for API rate limiting:
Rack::Attack.throttle('api/ip', limit: 300, period: 5.minutes) do |req| req.ip if req.path.start_with?('/api')end- Regular Updates: Keep Foodsoft, dependencies, and Docker image up to date.
- Audit Logs: Enable Rails logging for security audits:
RAILS_LOG_LEVEL=infoPerformance Optimization
- Database Connection Pool: Configure pool size in DATABASE_URL:
DATABASE_URL=postgresql://user:pass@host:5432/db?pool=25- Redis Caching: Enable fragment caching in production:
config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] }- Asset Compression: Ensure asset pipeline compression is enabled (default in production).
- Puma Workers: Configure Puma for multi-threading:
workers ENV.fetch("WEB_CONCURRENCY") { 2 }threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }threads threads_count, threads_count- Database Indexing: Ensure proper indexes on frequently queried columns.
Backup Strategy
- Database Backups: Schedule automated PostgreSQL backups:
# Create backup scriptpg_dump $DATABASE_URL > foodsoft_backup_$(date +%Y%m%d_%H%M%S).sql
# Create compressed backuppg_dump $DATABASE_URL | gzip > foodsoft_backup_$(date +%Y%m%d_%H%M%S).sql.gz- Volume Backups: Regularly backup persistent volumes containing uploads and documents.
- Configuration Backups: Keep versioned backups of
app_config.ymland environment variables. - Restore Testing: Periodically test backup restoration procedures.
Monitoring and Logging
- Application Logs: Monitor Rails logs for errors and performance issues.
- Sidekiq Monitoring: Check background job processing via Sidekiq web UI.
- Database Performance: Monitor PostgreSQL query performance and slow queries.
- Error Tracking: Integrate Sentry for error reporting:
SENTRY_DSN=your_sentry_dsn_here- Uptime Monitoring: Use external monitoring to track application availability.
- Health Checks: Foodsoft includes a
/healthendpoint for monitoring.
Scaling Recommendations
- Vertical Scaling: Start with 2 GB RAM, scale to 4-8 GB for larger cooperatives (500+ members).
- Database Scaling: Use read replicas for reporting queries and exports.
- Redis Scaling: Separate Redis instances for cache and Sidekiq if needed.
- Asset CDN: Consider CloudFront or Cloudflare for static asset delivery.
- Background Workers: Scale Sidekiq workers independently for email processing.
Troubleshooting
Database Connection Issues
Problem: Application fails to connect to PostgreSQL.
Solution:
- Verify
DATABASE_URLformat:postgresql://user:pass@host:port/database - Check PostgreSQL is deployed and accessible on port 8000 (for Klutch.sh TCP services)
- Ensure database exists:
rails db:create - Test connection from Rails console:
ActiveRecord::Base.connection
Redis Connection Issues
Problem: Background jobs not processing or cache errors.
Solution:
- Verify
REDIS_URLformat:redis://host:port/database_number - Check Redis is deployed and accessible
- Test connection:
redis-cli -u $REDIS_URL ping - Check Sidekiq logs for connection errors
Email Delivery Issues
Problem: Members not receiving notification emails.
Solution:
- Verify SMTP credentials and authentication method
- Check SMTP port (587 for TLS, 465 for SSL)
- Enable SMTP debug logging:
SMTP_LOG_LEVEL=debug - Test email delivery via Rails console:
ActionMailer::Base.mail( from: ENV['SMTP_SENDER_ADDRESS'], to: 'test@example.com', subject: 'Test', body: 'Test email').deliver_nowAsset Loading Issues
Problem: CSS, JavaScript, or images not loading properly.
Solution:
- Ensure
RAILS_SERVE_STATIC_FILES=trueis set - Precompile assets:
rails assets:precompile - Check asset paths in logs
- Verify public/system volume is properly mounted
Performance Issues
Problem: Slow page loads or timeouts.
Solution:
- Check database query performance in logs
- Enable query analysis:
RAILS_LOG_LEVEL=debug - Monitor Sidekiq queue for backlogs
- Increase Puma workers:
WEB_CONCURRENCY=4 - Add database indexes for slow queries
- Enable Redis caching
Migration Issues
Problem: Database migrations fail during deployment.
Solution:
- Run migrations manually:
rails db:migrate - Check migration logs for specific errors
- Rollback problematic migration:
rails db:rollback - Ensure database user has CREATE/ALTER permissions
Additional Resources
- Foodsoft GitHub Repository
- Foodsoft Wiki & Documentation
- Foodsoft Community Forum
- Development Guidelines
- Production Setup Documentation
- Foodsoft Hosting Platforms
- PostgreSQL Deployment Guide
- Redis Deployment Guide
- Ruby on Rails Deployment Guide
- Klutch.sh Persistent Volumes
- Klutch.sh Networking
You now have a fully functional Foodsoft deployment on Klutch.sh! Your cooperative members can manage orders, track finances, schedule work shifts, and coordinate with suppliers all through this powerful platform. Remember to regularly backup your database, keep the application updated, and monitor performance as your cooperative grows.