If you're on Zammad and finding it complex (Elasticsearch, PostgreSQL, Ruby), FreeScout offers a simpler alternative. This guide shows how to migrate without losing data.
Why Teams Switch from Zammad to FreeScout
Zammad is powerful but complex:
- Requires 4GB+ RAM (Elasticsearch is memory-hungry)
- Ruby on Rails is harder to customize than Laravel
- More features than most teams use
- Steeper learning curve
FreeScout is simpler:
- Runs on 1GB RAM
- Laravel is easier to understand
- Modular (add features as needed)
- Easier to customize
For teams that don't need omnichannel (SMS, WhatsApp, Twitter), FreeScout is overkill-free.
Migration Timeline
| Week | Activity | Hours | |---|---|---| | Week 1 | Set up FreeScout | 6–8 | | Week 2 | Export Zammad, import to FreeScout | 6–8 | | Week 3 | Parallel running (both systems) | 3–4 | | Week 4 | Cutover, training, Zammad shutdown | 2–3 |
Total: 3–4 weeks, 18–24 hours hands-on work.
Step 1: Set Up FreeScout (Week 1)
Choose an installation method:
- Ubuntu 22.04 — Most control
- DigitalOcean — Easiest
- Hetzner — Cheapest
- Docker — Fastest
Install, configure email, test. Don't add team members yet.
Step 2: Export Data from Zammad (Week 2)
Zammad stores data in PostgreSQL. You can export via API or direct database dump.
Option A: API Export (Recommended)
# Install Zammad API client
pip install requests
# Create export script
python3 << 'EOF'
import requests
import json
ZammadURL = "https://your-zammad.com"
Token = "your-api-token"
# Get all tickets
tickets = requests.get(
f"{ZammadURL}/api/v1/tickets",
headers={"Authorization": f"Token token={Token}"}
).json()
# Save to file
with open('zammad_tickets.json', 'w') as f:
json.dump(tickets, f, indent=2)
print(f"Exported {len(tickets)} tickets")
EOF
This exports all tickets as JSON.
Option B: Database Dump (Advanced)
pg_dump -h your-db-host -U zammad_user zammad_production > zammad_backup.sql
Full database backup (recovery option if API export fails).
Option C: Manual CSV Export
Zammad can export to CSV:
- Go to Zammad Admin → Data Management
- Click "Export"
- Select "Tickets"
- Download CSV
Slowest but most reliable.
Step 3: Transform Data
Zammad and FreeScout have different data structures.
Zammad → FreeScout Mapping:
| Zammad | FreeScout | Notes | |---|---|---| | Ticket | Conversation | Direct mapping | | Article | Conversation Thread | Direct mapping | | Customer | Customer | Direct mapping | | Group | Mailbox | Partial (Zammad groups → FreeScout mailbox) | | Organization | Organization | Map if using | | Tags | Tags | Direct mapping | | Custom fields | Custom fields | Need to recreate |
Transform Script (Python)
import json
import csv
from datetime import datetime
# Read Zammad export
with open('zammad_tickets.json') as f:
zammad_tickets = json.load(f)
# Transform for FreeScout import
freescout_conversations = []
for ticket in zammad_tickets:
conversation = {
'subject': ticket['title'],
'customer_email': ticket['customer_email'],
'customer_name': ticket['customer']['fullname'],
'status': map_status(ticket['state']),
'priority': map_priority(ticket['priority']),
'created_at': ticket['created_at'],
'updated_at': ticket['updated_at'],
'messages': []
}
# Add ticket messages
for article in ticket['articles']:
conversation['messages'].append({
'body': article['body'],
'author': article['author']['fullname'],
'created_at': article['created_at'],
'is_customer': article['sender'] == 'Customer'
})
freescout_conversations.append(conversation)
# Save as JSON for import
with open('freescout_import.json', 'w') as f:
json.dump(freescout_conversations, f, indent=2)
def map_status(zammad_status):
mapping = {
'new': 'open',
'open': 'open',
'pending': 'pending',
'closed': 'closed',
'merged': 'closed'
}
return mapping.get(zammad_status, 'open')
def map_priority(priority):
mapping = {
'1 low': 'low',
'2 normal': 'medium',
'3 high': 'high',
'4 very high': 'urgent'
}
return mapping.get(priority, 'medium')
Step 4: Import into FreeScout
Create Mailbox First
- Settings → Mailboxes → Create Mailbox
- Name: "Migrated Tickets"
- Email: your support email
- Configure IMAP/SMTP
Import Script (Laravel/PHP)
<?php
// artisan tinker
$json = json_decode(file_get_contents('freescout_import.json'), true);
$mailbox_id = 1; // Your mailbox ID
foreach ($json as $conv_data) {
// Create or get customer
$customer = Customer::where('email', $conv_data['customer_email'])->first();
if (!$customer) {
$customer = Customer::create([
'email' => $conv_data['customer_email'],
'first_name' => explode(' ', $conv_data['customer_name'])[0],
'last_name' => explode(' ', $conv_data['customer_name'])[1] ?? '',
]);
}
// Create conversation
$conversation = Conversation::create([
'mailbox_id' => $mailbox_id,
'customer_id' => $customer->id,
'subject' => $conv_data['subject'],
'status' => $conv_data['status'],
'priority' => $conv_data['priority'],
'created_at' => $conv_data['created_at'],
'updated_at' => $conv_data['updated_at'],
]);
// Add messages
foreach ($conv_data['messages'] as $msg) {
ConversationThread::create([
'conversation_id' => $conversation->id,
'type' => $msg['is_customer'] ? 'customer' : 'agent',
'body' => $msg['body'],
'created_at' => $msg['created_at'],
]);
}
}
echo "Import complete!";
?>
Run in FreeScout:
cd /var/www/freescout
php artisan tinker < import_script.php
Step 5: Validate Import
Check FreeScout for:
- [ ] All tickets imported (count matches Zammad)
- [ ] Subjects correct
- [ ] Messages in order
- [ ] Customers created
- [ ] Dates preserved
- [ ] Status mapping correct
Sample 20–30 tickets and compare to Zammad.
Step 6: Parallel Running (Week 3)
Run both systems for 1–2 weeks:
- Zammad: Read-only (no new tickets)
- FreeScout: All new tickets
- Temporary email: Point new requests to FreeScout
Step 7: Cutover (Week 4)
Pre-Cutover
- [ ] All team trained on FreeScout
- [ ] Email working (SMTP/IMAP tested)
- [ ] Backups created
- [ ] Queue workers running
Cutover Steps
- Export final tickets from Zammad (anything created during parallel period)
- Import to FreeScout
- Update email addresses everywhere (website, docs, auto-responder)
- Disable temporary email address
- Cancel Zammad
Cost Analysis
Zammad Annual Cost:
- Hosting (4GB RAM VPS): $40–$80/month = $480–$960/year
- Elasticsearch cluster (if managed): $50+/month = $600+/year
- Database backups: $20–$50/month = $240–$600/year
- Total: $1,320–$2,160/year
FreeScout Annual Cost:
- Setup: $150 one-time
- Hosting (1GB RAM VPS): $6–$12/month = $72–$144/year
- Total: $222–$294/year
Year 1 Savings: $1,098–$1,938 Year 2+ Savings: $1,320–$2,160/year
Even if migration takes 30 hours at $50/hour ($1,500 cost), you break even in 1 year.
Zammad → FreeScout Decision Matrix
| Factor | Zammad | FreeScout | Winner | |---|---|---|---| | Omnichannel (SMS, WhatsApp) | ✅ Built-in | ❌ Limited | Zammad | | Simplicity | ❌ Complex | ✅ Simple | FreeScout | | Resource usage | ❌ 4GB+ | ✅ 1GB | FreeScout | | Cost | ❌ $1,320+/yr | ✅ $294/yr | FreeScout | | Email + chat only | ❌ Overkill | ✅ Perfect | FreeScout | | Customization | ⚠️ Ruby | ✅ Laravel | FreeScout |
Choose FreeScout if: You use email + chat, want simplicity and cost savings Stay on Zammad if: You need omnichannel support
Common Issues
| Issue | Cause | Fix | |---|---|---| | "Import fails" | JSON format wrong | Check script output, validate JSON | | "Emails don't match" | Formatting differences | Normalize: lowercase, trim whitespace | | "Status wrong" | Mapping incorrect | Update map_status() function | | "Messages out of order" | Timestamp issues | Verify created_at format |
Summary
Migrating from Zammad to FreeScout is straightforward:
- Set up FreeScout (6–8 hours)
- Export Zammad data (1–2 hours)
- Transform and import (2–4 hours)
- Test thoroughly (1–2 hours)
- Parallel run (1–2 weeks)
- Cutover (1 day)
Total effort: ~20 hours Payoff: $1,000+/year in savings
Want expert help migrating from Zammad to FreeScout?
We handle the full FreeScout installation on your server — SSL, email, security hardening, and a 1-hour onboarding call. Done in 24 hours.
One-time fee · 30-day support · Money-back guarantee
The complexity of Zammad might be worth it for omnichannel. For email + chat, FreeScout wins.
Resources
- Zammad Documentation — official Zammad guide
- FreeScout GitHub — source code and releases
- Zammad REST API — for data export
- FreeScout Module Marketplace — available modules