Migrate Your Free Tier Library to a Separate Data Volume
One-time migration guide for existing Free tier users to move library data from the root volume to a new persistent EBS data volume. Required before upgrading to an AMI released after 2026-05-27.
Migrate Your Free Tier Library to a Separate Data Volume
⚠️ Read before proceeding
This guide describes a process that can cause data loss or extended downtime if steps are missed or executed out of order.
Before you start:
- Back up your data and verify the backup is complete and restorable.
- Test the full process on a non-production environment before touching your live library.
- Schedule a maintenance window and notify users if this affects a production system.
Not confident? KohaSupport can guide you through migrations and upgrades. Contact us for professional assistance →
Starting with AMIs released after 2026-05-27, the Free tier uses a separate persistent EBS data volume for your library data — the same model as Standard tier. Your MySQL database, Koha configuration, and SSL certificates live on this separate volume, which survives instance replacements and AMI upgrades.
If your stack was created before this date, your data is still on the root volume and you need to perform this one-time migration before upgrading your AMI. If you upgrade without migrating, your library catalog will be replaced by a fresh empty Koha installation.
What changes
| Before (legacy) | After (migration complete) | |
|---|---|---|
| Root volume | 20 GiB — OS + Koha data | 10 GiB — OS only |
| Data volume | None | 20 GiB (or larger) separate EBS vol |
| Data survives instance replacement? | ❌ No | ✅ Yes — DeletionPolicy: Retain |
| AMI upgrades | Data lost | Safe — swap instance, keep data |
This guide is for CloudFormation-based deployments only. If you launched your Koha server directly from the EC2 Launch Wizard (without CloudFormation), the steps below — which use CloudFormation stack updates and Auto Scaling Groups — do not apply. For a direct EC2 deployment, the equivalent process is:
- Create a new EBS volume in the same Availability Zone as your instance
- Attach it as
/dev/sdb- Format and mount it:
sudo mkfs.ext4 /dev/xvdb && sudo mkdir -p /kohadata && sudo mount /dev/xvdb /kohadata- Copy Koha data to it and update
/etc/fstabfor persistence- Reconfigure Koha to use
/kohadataThis is an advanced procedure. Contact KohaSupport if you need assistance with a direct EC2 data volume migration.
Before you start
- This migration requires approximately 30–45 minutes and a brief period of Koha downtime
- You need AWS Console or CLI access
- Run all instance commands via EC2 Instance Connect or Session Manager (SSM)
Step 1 — Take a safety backup
Connect to your instance and run koha-dump:
sudo koha-dump library
ls -lh /var/spool/koha/library/
Note the backup filename (e.g. library-2026-05-29.tar.gz). This stays on the root volume of the current instance as a safety net — if the migration fails before Step 4, you can restore from it.
Step 2 — Update your CloudFormation stack to add the data volume
This step creates the new persistent EBS volume and registers it in SSM. Your current instance keeps running during this step.
- Go to CloudFormation → select your stack → Update stack
- Choose Replace existing template
- Enter the new Free tier template URL (from your AWS Marketplace subscription launch page)
- Set EBSVolumeSize to your desired data volume size (default: 20 GiB)
- Leave all other parameters unchanged → Update stack
- Wait for
UPDATE_COMPLETE
Confirm the volume was created: Outputs tab → DataVolumeId — note this value.
Step 3 — Copy your library data to the new volume
Run this on your currently running instance. It attaches the new empty volume, copies your Koha data onto it, and marks it as initialised so the new AMI skips its first-boot copy.
Connect via Session Manager or EC2 Instance Connect and run:
# IMDSv2 helper (required on instances with HttpTokens=required)
TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
get_meta() { curl -sf -H "X-aws-ec2-metadata-token: $TOKEN" "http://169.254.169.254/latest/meta-data/$1"; }
INSTANCE_ID=$(get_meta instance-id)
REGION=$(get_meta placement/region)
export AWS_DEFAULT_REGION="$REGION"
STACK=$(aws ec2 describe-tags \
--filters "Name=resource-id,Values=$INSTANCE_ID" \
"Name=key,Values=aws:cloudformation:stack-name" \
--query 'Tags[0].Value' --output text)
VOLUME_ID=$(aws ssm get-parameter \
--name "/koha/$STACK/data-volume-id" \
--region "$REGION" --query 'Parameter.Value' --output text)
echo "Attaching $VOLUME_ID to $INSTANCE_ID"
aws ec2 attach-volume \
--volume-id "$VOLUME_ID" --instance-id "$INSTANCE_ID" \
--device /dev/xvdg --region "$REGION"
VOL_SERIAL=$(echo "$VOLUME_ID" | tr -d '-')
DEVICE=""
for i in $(seq 1 60); do
DEVICE=$(lsblk --output NAME,SERIAL --noheadings 2>/dev/null \
| awk -v s="$VOL_SERIAL" '$2==s {print "/dev/"$1; exit}')
[ -z "$DEVICE" ] && [ -b "/dev/xvdg" ] && DEVICE="/dev/xvdg"
[ -n "$DEVICE" ] && [ -b "$DEVICE" ] && break
sleep 3
done
echo "Block device: $DEVICE"
sudo mkfs.ext4 -L kohadata "$DEVICE"
sudo mkdir -p /mnt/kohadata-migration
sudo mount "$DEVICE" /mnt/kohadata-migration
sudo systemctl stop mysql 2>/dev/null || sudo systemctl stop mariadb 2>/dev/null
sudo rsync -a /var/lib/mysql/ /mnt/kohadata-migration/mysql/
sudo rsync -a /etc/koha/sites/ /mnt/kohadata-migration/koha-sites/ 2>/dev/null || true
sudo rsync -a /var/lib/koha/ /mnt/kohadata-migration/koha-lib/ 2>/dev/null || true
[ -d /etc/letsencrypt ] && sudo rsync -a /etc/letsencrypt/ /mnt/kohadata-migration/letsencrypt/ || true
sudo touch /mnt/kohadata-migration/.kohadata.initialized
echo "=== Migration copy complete ==="
sudo ls -la /mnt/kohadata-migration/
sudo systemctl start mysql 2>/dev/null || sudo systemctl start mariadb 2>/dev/null
sudo umount /mnt/kohadata-migration
aws ec2 detach-volume --volume-id "$VOLUME_ID" --region "$REGION"
echo "Volume detached — ready for instance replacement"
Wait ~30 seconds, then confirm the volume is detached:
aws ec2 describe-volumes --volume-ids "$VOLUME_ID" \
--query 'Volumes[0].State' --output text --region "$REGION"
# Expected: available
Step 4 — Replace the instance
Your library data is now on the data volume. Terminate the old instance so the ASG launches a replacement with the new AMI.
Because DisableApiTermination is set, use the Auto Scaling Group — not direct EC2 terminate.
Via AWS Console:
- EC2 → Auto Scaling Groups → find
<stack-name>-asg - Instance Management tab → select your instance
- Actions → Terminate → Do not decrease desired capacity → confirm
Via AWS CLI:
STACK=<your-stack-name>
REGION=us-east-1
ASG=$(aws cloudformation describe-stack-resources \
--stack-name "$STACK" \
--query "StackResources[?ResourceType=='AWS::AutoScaling::AutoScalingGroup'].PhysicalResourceId" \
--output text --region "$REGION")
INSTANCE=$(aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-names "$ASG" \
--query "AutoScalingGroups[0].Instances[0].InstanceId" \
--output text --region "$REGION")
aws autoscaling terminate-instance-in-auto-scaling-group \
--instance-id "$INSTANCE" \
--no-should-decrement-desired-capacity \
--region "$REGION"
Wait 5–8 minutes. The new instance boots, attaches the data volume, sees the .kohadata.initialized marker file, and starts Koha using your migrated library data.
Step 5 — Verify
Connect to the new instance and check:
# Data volume mounted at /kohadata
df -h | grep kohadata
# MySQL running from data volume
sudo mysql -e "SELECT @@datadir;"
# Plack running
sudo koha-plack --status library
# Confirm migration boot (should NOT see "First boot: initialising data volume")
sudo grep "upgrade boot\|First boot\|kohadata.initialized" /var/log/koha-userdata.log
Expected:
# df:
/dev/nvme1n1 20G ... /kohadata
# @@datadir:
/kohadata/mysql/
# userdata log:
=== AMI upgrade boot: existing library data detected — skipping initialisation ===
Open your Koha OPAC and Staff Interface to confirm your catalog is intact.
Rollback
Before Step 4 (old instance still running): Run sudo umount /mnt/kohadata-migration, detach the volume, restart MySQL. Your data is still on the root volume.
After Step 4: The data volume has your migrated data. If Koha is not starting, check /var/log/koha-userdata.log and restart services: service memcached restart && koha-plack --restart library.
EBS volume size
The EBSVolumeSize parameter controls the data volume size (not the root — root is always 10 GiB from the AMI). Default: 20 GiB. You can increase this later via EC2 → Volumes → Modify volume, but cannot decrease it.
Next Steps
Was this article helpful?