Skip to content

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:


Installation and Setup

Step 1: Create Your Project Directory

First, create a new directory for your Foodsoft deployment project:

Terminal window
mkdir foodsoft-klutch
cd foodsoft-klutch
git init

Step 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 gems
EXPOSE 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 endpoint
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
CMD curl -f http://localhost:3000/health || exit 1

Note: 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 Configuration
DATABASE_URL=postgresql://username:password@postgres-host:5432/foodsoft_production
# Redis Configuration for Background Jobs
REDIS_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 Settings
RAILS_ENV=production
RAILS_LOG_TO_STDOUT=true
RAILS_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 Notifications
SMTP_ADDRESS=smtp.example.com
SMTP_PORT=587
SMTP_DOMAIN=example.com
SMTP_USER_NAME=notifications@example.com
SMTP_PASSWORD=your_smtp_password
SMTP_AUTHENTICATION=plain
SMTP_ENABLE_STARTTLS_AUTO=true
# Email Sender
SMTP_SENDER_ADDRESS=noreply@example.com
SMTP_SENDER_NAME=Your Food Coop
# Application Host
FOODSOFT_HOST=foodsoft.example.com
# Optional: Error Tracking
# SENTRY_DSN=your_sentry_dsn_here
# Optional: Time Zone
TZ=Europe/Berlin

Step 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 environments
default: &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 environment
production:
<<: *defaults

Step 5: Initialize Git Repository

Before deploying, commit your files to Git:

Terminal window
# Create .gitignore
cat > .gitignore << EOF
.env
*.log
tmp/*
!tmp/.keep
public/system/*
!public/system/.keep
EOF
# Add and commit files
git add Dockerfile .env.example config/app_config.yml .gitignore
git commit -m "Initial Foodsoft setup for Klutch.sh deployment"

Deploying to Klutch.sh

Step 1: Push to GitHub

  1. Create a new repository on GitHub.
  2. Push your local repository:
Terminal window
git remote add origin https://github.com/yourusername/foodsoft-klutch.git
git branch -M master
git push -u origin master

Step 2: Create a New App on Klutch.sh

  1. Log in to your Klutch.sh dashboard.
  2. Click New App and select your GitHub repository.
  3. Klutch.sh will automatically detect your Dockerfile and prepare for deployment.
  4. Select HTTP traffic since Foodsoft is a web application.
  5. 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:

Terminal window
# 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 Environment
RAILS_ENV=production
RAILS_LOG_TO_STDOUT=true
RAILS_SERVE_STATIC_FILES=true

SMTP Configuration:

Terminal window
SMTP_ADDRESS=smtp.gmail.com
SMTP_PORT=587
SMTP_DOMAIN=example.com
SMTP_USER_NAME=your-email@gmail.com
SMTP_PASSWORD=your-app-password
SMTP_AUTHENTICATION=plain
SMTP_ENABLE_STARTTLS_AUTO=true
SMTP_SENDER_ADDRESS=noreply@example.com
SMTP_SENDER_NAME=Your Food Coop

Optional Variables:

Terminal window
# Application Host
FOODSOFT_HOST=your-app.klutch.sh
# Time Zone
TZ=America/New_York
# Multi-Coop (if hosting multiple cooperatives)
FOODSOFT_MULTI_COOP_INSTALL=false

Mark 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:

  1. In the Klutch.sh dashboard, navigate to Volumes.
  2. 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

  1. Click Deploy in the Klutch.sh dashboard.
  2. Klutch.sh will build your Docker image and deploy the application.
  3. Monitor the build logs for any errors.
  4. 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:

  1. Access the application container via Klutch.sh terminal or SSH.
  2. Run database migrations:
Terminal window
rails db:schema:load
rails db:seed

Note: The seed data creates an initial admin user. Check the Foodsoft documentation for default credentials.

Step 7: Create Your First Admin User

  1. Access your deployed Foodsoft instance at https://your-app.klutch.sh.
  2. Use the default admin credentials from the seed data, or create a new admin user via Rails console:
Terminal window
# Access Rails console in the container
rails console
# Create admin user
User.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:

Terminal window
DATABASE_URL=postgresql://username:password@host:port/database_name

For Klutch.sh-hosted PostgreSQL:

Terminal window
DATABASE_URL=postgresql://myuser:mypassword@postgres-app.klutch.sh:8000/foodsoft_production

Database Performance Tips:

  • Enable connection pooling with ?pool=25 in 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:

Terminal window
REDIS_URL=redis://host:port/database_number

For Klutch.sh-hosted Redis:

Terminal window
REDIS_URL=redis://redis-app.klutch.sh:8000/0

Redis 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:

Terminal window
SMTP_ADDRESS=smtp.gmail.com
SMTP_PORT=587
SMTP_DOMAIN=yourdomain.com
SMTP_USER_NAME=notifications@yourdomain.com
SMTP_PASSWORD=your_app_password
SMTP_AUTHENTICATION=plain
SMTP_ENABLE_STARTTLS_AUTO=true
SMTP_SENDER_ADDRESS=noreply@yourdomain.com
SMTP_SENDER_NAME=Your Food Coop

Recommended 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:

Terminal window
FOODSOFT_MULTI_COOP_INSTALL=true
FOODSOFT_APP_CONFIG_PATH=/usr/src/app/config/app_config.yml

Create config/app_config.yml with multiple coop configurations:

# Multi-coop configuration
use_multi_coop: true
# Coop 1
coop_one:
name: First Food Coop
database:
adapter: postgresql
database: foodsoft_coop1
# ... other database settings
homepage: https://coop1.example.com
# Coop 2
coop_two:
name: Second Food Coop
database:
adapter: postgresql
database: foodsoft_coop2
homepage: https://coop2.example.com

Background 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 credentials
api_url = 'https://your-app.klutch.sh/api/v1'
api_token = 'your_api_token_here'
# Fetch current orders
uri = 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']})"
end
else
puts "Error: #{response.code} - #{response.body}"
end

Python - Create New Article

import requests
import json
# Configure API credentials
api_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 article
article_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 credentials
const apiUrl = 'https://your-app.klutch.sh/api/v1';
const apiToken = 'your_api_token_here';
// Fetch member account balance
async 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;
}
}
// Usage
getMemberBalance(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 details
function 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;
}
}
// Usage
getOrderDetails(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:

Terminal window
docker-compose up -d

Access Foodsoft at http://localhost:3000.


Production Best Practices

Security Hardening

  1. Strong Secret Key: Generate a secure SECRET_KEY_BASE:
Terminal window
openssl rand -hex 64
  1. Database Credentials: Use strong passwords and limit database access to the application IP.
  2. HTTPS Only: Ensure all traffic uses HTTPS (Klutch.sh provides this automatically).
  3. Rate Limiting: Configure Rack::Attack in Rails for API rate limiting:
config/initializers/rack_attack.rb
Rack::Attack.throttle('api/ip', limit: 300, period: 5.minutes) do |req|
req.ip if req.path.start_with?('/api')
end
  1. Regular Updates: Keep Foodsoft, dependencies, and Docker image up to date.
  2. Audit Logs: Enable Rails logging for security audits:
Terminal window
RAILS_LOG_LEVEL=info

Performance Optimization

  1. Database Connection Pool: Configure pool size in DATABASE_URL:
Terminal window
DATABASE_URL=postgresql://user:pass@host:5432/db?pool=25
  1. Redis Caching: Enable fragment caching in production:
config/environments/production.rb
config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] }
  1. Asset Compression: Ensure asset pipeline compression is enabled (default in production).
  2. Puma Workers: Configure Puma for multi-threading:
config/puma.rb
workers ENV.fetch("WEB_CONCURRENCY") { 2 }
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count
  1. Database Indexing: Ensure proper indexes on frequently queried columns.

Backup Strategy

  1. Database Backups: Schedule automated PostgreSQL backups:
Terminal window
# Create backup script
pg_dump $DATABASE_URL > foodsoft_backup_$(date +%Y%m%d_%H%M%S).sql
# Create compressed backup
pg_dump $DATABASE_URL | gzip > foodsoft_backup_$(date +%Y%m%d_%H%M%S).sql.gz
  1. Volume Backups: Regularly backup persistent volumes containing uploads and documents.
  2. Configuration Backups: Keep versioned backups of app_config.yml and environment variables.
  3. Restore Testing: Periodically test backup restoration procedures.

Monitoring and Logging

  1. Application Logs: Monitor Rails logs for errors and performance issues.
  2. Sidekiq Monitoring: Check background job processing via Sidekiq web UI.
  3. Database Performance: Monitor PostgreSQL query performance and slow queries.
  4. Error Tracking: Integrate Sentry for error reporting:
Terminal window
SENTRY_DSN=your_sentry_dsn_here
  1. Uptime Monitoring: Use external monitoring to track application availability.
  2. Health Checks: Foodsoft includes a /health endpoint for monitoring.

Scaling Recommendations

  1. Vertical Scaling: Start with 2 GB RAM, scale to 4-8 GB for larger cooperatives (500+ members).
  2. Database Scaling: Use read replicas for reporting queries and exports.
  3. Redis Scaling: Separate Redis instances for cache and Sidekiq if needed.
  4. Asset CDN: Consider CloudFront or Cloudflare for static asset delivery.
  5. Background Workers: Scale Sidekiq workers independently for email processing.

Troubleshooting

Database Connection Issues

Problem: Application fails to connect to PostgreSQL.

Solution:

  • Verify DATABASE_URL format: 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_URL format: 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_now

Asset Loading Issues

Problem: CSS, JavaScript, or images not loading properly.

Solution:

  • Ensure RAILS_SERVE_STATIC_FILES=true is 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


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.