"We're moving off Atlassian / Salesforce / Slack to PMA. Migrate the data, keep the team productive, retire the SaaS subscriptions on a planned timeline."
The shape of every SaaS-to-PMA migration is the same: export from the SaaS, transform to the target service's format, import. The trick is sequencing so the team isn't down.
PMA's services cover most SaaS categories:
| SaaS being retired | PMA service |
|---|---|
| Atlassian Jira | Redmine |
| Atlassian Confluence | Wiki.js |
| Slack / Microsoft Teams | Mattermost |
| Zendesk / Freshdesk / Intercom | Zammad |
| Salesforce | EspoCRM (lightweight) or ERPNext CRM (full) |
| Salesforce Sales Cloud | ERPNext |
| Google Drive shared docs | Wiki.js + AFFiNE for notes |
| Mailchimp / HubSpot | Mautic |
| Datadog / New Relic | Grafana + Prometheus + Loki |
We'll walk through Jira → Redmine as the worked example. Other migrations follow the same pattern: export, transform, import, validate, switch DNS.
Spin up PMA with the target service running. Let the team play
with it for a week before any data moves. This catches "this UI
isn't what we expected" issues before the migration window.
# On a prod-class host
just bootstrap-local enterprise prod
# Redmine is now running at https://redmine-<id>.<tunnel>
Have the team create test projects/tickets in the new Redmine.
Confirm SSO works, attachments work, email notifications work.
Use Jira's built-in export:
Jira admin → System → External system import / export → Export
Choose XML or CSV. CSV is easier for transformation; XML preserves more metadata (attachments, sub-tasks, links).
Or use the REST API for selective export:
curl -u admin:$JIRA_TOKEN \
"https://your-org.atlassian.net/rest/api/3/search?jql=project=PROJ&maxResults=1000" \
> jira-export.json
The PMA team has a transformation script (community-contributed):
# Lives in scripts/migrate/jira-to-redmine.ts
bun scripts/migrate/jira-to-redmine.ts \
--input jira-export.json \
--output redmine-import.json \
--map-users users-mapping.csv
users-mapping.csv maps Jira usernames → Redmine usernames (since SSO is Authentik, this is "Jira email → Authentik user"). For users that don't yet exist, create them in Authentik first (or via just user-add-all).
Transformation handles:
bun scripts/migrate/redmine-import.ts \
--input redmine-import.json \
--target ${REDMINE_URL} \
--api-key ${REDMINE_API_KEY}
For thousands of tickets, batch by project. The script supports --resume-from <id> for retrying failed batches.
Sanity checks:
# Count check
echo "Jira had $(jq '.total' jira-export.json) issues"
just redmine-psql -c "SELECT COUNT(*) FROM issues;"
# Spot check
# Open the new Redmine, find 5-10 random tickets, verify:
# - Title + description match
# - Assignee correct
# - Comments preserved with original timestamps
# - Attachments downloadable
Have a couple of team members do their own spot-checks on the projects they're closest to.
Update internal documentation to point at Redmine URLs instead of Jira URLs. If you have a DNS alias (tickets.your-org.com) pointing at Jira, repoint it at Redmine.
For inbound links from external systems (GitHub commits, customer emails, etc.), Jira links stop working when the subscription ends. Plan ahead — either keep Jira read-only for a transition period, or accept the broken links and document the migration in a public-facing note.
Don't cancel the SaaS the same day. Keep Jira read-only for 30-90 days as a safety net. Then:
Atlassian admin → Billing → Cancel subscription
Watch for any team member screaming "I needed that one Jira link from January" — that's your signal to extend the read-only period.
| From → to | Tool / approach |
|---|---|
| Confluence → Wiki.js | Confluence export → HTML → markdown conversion → bulk upload via Wiki.js API |
| Slack → Mattermost | Mattermost bundles mmctl import slack — official tool, handles channels + DMs + files |
| Zendesk → Zammad | Zammad has built-in Zendesk importer (admin UI: Settings → Import) |
| Salesforce → EspoCRM | EspoCRM has CSV import; transform the Salesforce export with field mappings |
| Mailchimp → Mautic | Mautic has Mailchimp CSV importer |
Each PMA service's /pma/services/<svc> page has migration-specific notes for the SaaS sources it commonly replaces.