ODK Central Domain Change: A Painless Backup and Restore Tutorial
Tags: docker, odk, backup, bash
Published: 2023-10-21 20:00:00

---
title: "ODK Central Domain Change: A Painless Backup and Restore Tutorial"
description: "Learn how to migrate your ODK Central installation to a new domain name. This step-by-step tutorial covers backing up, restoring, and updating Redis databases to change domain names."
tags: docker, odk, backup, bash
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n20k8ekefh6bvfyg5y04.png
published: true
---

## Introduction

ODK Central is a powerful tool for collecting, managing, and analyzing data using mobile devices. However, there may be situations where you need to move your ODK deployment from one server to another, such as changing hosting providers, upgrading hardware, or switching domains.

This tutorial will walk you through the steps of creating a direct backup of your ODK Central database, copying the backup file and other essential data to the destination server, and restoring the backup file on the new server. The tutorial will also show you how to update the domain names in the Enketo Redis databases and config files to avoid errors when previewing forms on the new server.

By following this tutorial, you will be able to migrate your ODK deployment from one server to another with minimal downtime and data loss.

## Prerequisites

Before you begin, ensure that you have the following prerequisites:

- A running instance of ODK Central on the source and destination servers.

- Both source and destination servers are running the same version of ODK Central.

- Access to the command line interface (CLI) of both source and destination servers.

- Sufficient disk space on both servers to store the backup files and other essential data.

If you have met these requirements, you will be able to follow this tutorial easily and avoid errors or issues that may arise during the backup and restore process.

## Backup ODK Central Main Data on the Source Server

1. Log in to your ODK Central source server.

1. From the Terminal window, create a folder _odk-backups_ for the ODK Central backup files in the logged-in user's home folder. You can use a different name that follows your folder naming conventions.

    ```bash
    mkdir ~/odk-backups
    ```

1. Run the command below to perform a direct backup of your ODK Central database. Replace **`backup-password`**, **`your-odk-username@your-odk-domain-name`**, **`your-odk-domain-name`**, and **`backup-filename.zip`** placeholder values with actual values for your ODK Central installation.

    ```bash
      curl -X POST -H "Content-Type: application/json" -d '{"passphrase": "backup-password"}' -su your-odk-username@your-odk-domain-name https://your-odk-domain-name/v1/backup --output ~/odk-backups/backup-filename.zip
    ```

    **`{"passphrase": "backup-password"}`**: password that will be used to encrypt the backup file
    **`your-odk-username@your-odk-domain-name`**: Username that you use to log into the ODK Central web interface
    **`your-odk-domain-name`**: Registered domain name for your ODK Central installation
    **`backup-filename.zip`**: The name that will be assigned to the backup file.

    You will be asked to provide your user password before the command can run. The password you type will be the password for the user that you use to log into the ODK Central web interface

1. Wait for the backup process to run to completion.

    Below is an example of commands contained in the previous steps.

    ```bash
    root:~#
    root:~# mkdir odk-backups
    root:~#
    root:~# curl -X POST -H "Content-Type: application/json" -d '{"passphrase": "bk202309"}' -k  -u testapp@example.com https://odk-src.kagundajm.codes/v1/backup --output ~/odk-backups/odk-bk20230913-08AM.zip
    Enter host password for user 'testapp@example.com':
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                    Dload  Upload   Total   Spent    Left  Speed
    100  481M    0  481M    0    26  4819k      0 --:--:--  0:01:42 --:--:-- 10.4M
    root:~#
    root:~# cd odk-backups/
    root:~#
    root:~/odk-backups# ls -la
    total 493356
    drwxr-xr-x  2 root root      4096 Sep 12 05:17 .
    drwx------ 10 root root      4096 Sep 12 05:17 ..
    -rw-r--r--  1 root root 505183986 Sep 12 05:19 odk-bk20230913-08AM.zip
    root:~/odk-backups#
    ```

## List ODK Central Docker Containers

This step is not mandatory, but it helps you understand the source of Docker container names used in this tutorial.

1. On the source server, change directory to the ODK Central installation folder

    ```bash
    cd central
    ```

1. List names and state of ODK Central Docker containers using the following command:

    ```bash
    docker ps --format 'table {{.Names}}\t{{.State}}'
    ```

    **`--format`** option will list names and headers of the required fields. **`\t`** is a tab character to separate the fields.

    Running the command will output a list similar to the following:

    ```bash
    NAMES                          STATE
    central-nginx-1                running
    central-service-1              running
    central-enketo-1               running
    central-pyxform-1              running
    central-enketo_redis_cache-1   running
    central-mail-1                 running
    central-postgres14-1           running
    central-enketo_redis_main-1    running
    ```

## Backup Enketo Redis Data and Secrets

Redis stores data in memory (RAM) which allows for fast data access and retrieval. The ODK Central installation includes two Redis database instances; **`central-enketo_redis_main-1`** and **`central-enketo_redis_cache-1`**. To backup the Redis databases, you run commands to flush the in-memory data to disk.

1. Log in to your ODK Central source server if you had logged out.

1. Change directory to the ODK Central installation folder

    ```bash
    cd ~/central
    ```

1.  Flush in-memory data to disk on the main instance of Enketo Redis database by running the following command.

    ```bash
    docker exec central-enketo_redis_main-1 redis-cli -p 6379 save
    ```

    The command instructs Docker to execute the Redis Command Line Interface (CLI) tool (**`redis-cli`** )within the Docker container named **central-enketo_redis_main-1** which is listening on port 6379. **`save`** creates a snapshot of the current state of the Redis database and writes it to disk.

    The server should display **OK** after the command completes and a backup file named _enketo-main.rdb_ will be created in the _/data/_ folder of the **`central-enketo_redis_main-1`** container.

1. Copy the _enketo-main.rdb_ file from the Docker container to the backup folder you created.

    ```bash
    docker cp  central-enketo_redis_main-1:/data/enketo-main.rdb ~/odk-backups/enketo-main.rdb
    ```

1. Flush in-memory data to disk on the cache instance of Enketo Redis database.

    ```bash
    docker exec central-enketo_redis_cache-1 redis-cli -p 6380 save
    ```

     The server should display **OK** after the command completes and a backup file named _enketo-cache.rdb_ will be created in the _/data/_ folder of the **`central-enketo_redis_cache-1`** container.

1. Copy the _enketo-cache.rdb_ backup file from the Docker container to the backup folder you created.

    ```bash
    docker cp  central-enketo_redis_cache-1:/data/enketo-cache.rdb ~/odk-backups/enketo-cache.rdb
    ```

1. The **`central-service-1`** container has a _/etc/secrets_ folder which contains files used to store secret keys for the Enketo server. These keys are used to encrypt and decrypt data. Copy the _secrets_ folder to the _odk-backups_ folder.

    ```bash
    docker cp central-service-1:/etc/secrets ~/odk-backups
    ```

    The following is an example sequence of the commands and outputs for the above steps.

      ```bash
      root:~/odk-backups# cd ~/central
      root:~/central#
      root:~/central# docker exec central-enketo_redis_main-1 redis-cli -p 6379 save
      OK
      root:~/central# docker cp  central-enketo_redis_main-1:/data/enketo-main.rdb ~/odk-backups/enketo-main.rdb
      Successfully copied 39.9kB to /root/odk-backups/enketo-main.rdb
      root:~/central#
      root:~/central# docker exec central-enketo_redis_cache-1 redis-cli -p 6380 save
      OK
      root:~/central#
      root:~/central# docker cp  central-enketo_redis_cache-1:/data/enketo-cache.rdb ~/odk-backups/enketo-cache.rdb
      Successfully copied 2.05kB to /root/odk-backups/enketo-cache.rdb
      root:~/central#
      root:~/central# docker cp central-service-1:/etc/secrets ~/odk-backups
      Successfully copied 4.61kB to /root/odk-backups
      root:~/central#
      ```

1. Copy Enketo config file from Docker container to a text file in the backup folder.

    ```bash
    docker cp central-enketo-1:/srv/src/enketo_express/config/config.json ~/odk-backups/enketo-config-keys.txt
    ```

1. Open the text file using the **nano** editor.

    ```bash
    nano  ~/odk-backups/enketo-config-keys.txt
    ```

1. Using **Control** + **K** (**^K**) keys, remove all key/pair values in the text file except "encryption key", "less secure encryption key", and "api key". After you have completed removing all other key/values pairs, your text file should contain entries similar to the following:

    ```bash
    "encryption key": "JAUzy4moYjA284rORjMcd2FIQCEdniSx9rmYZrqZSdxwzmG1WY6Apx6QAphgAFc9",
    "less secure encryption key": "KgyIvGRFtnctP99qGzi0ii1GVg1j453U",
    "api key": "YJXZRqiOirbSijVROqnh3HGCvuhEwhmKFrkyDkwl4HHIYo0Z3YNitfQH1McNp7srlf1RcvCfXX8vzCVoINW7UgZAIT6jbjR8gxaYJR3wzE8dNGZgdEitwl7cs2abkWUA",
    ```

1. Press **Control** + **O** (**^O**)  to save the changes.

1. Press **ENTER** key to confirm  the file name.

1. Press **Control** + **X** (**^X**)  to close the editor.

1. Log out of the source server

## Copy Backups From Source Server To Destination Server

1. Log in to your ODK Central destination server.

1. If you are using username/password to connect to the source server, run the  command below to copy the _~/odk-backups_ folder to the destination server. You will be prompted to type the user's password before the `rsync` command starts the syncing process.

    ```bash
    rsync -avz user@remote_server:~/odk-backups  ~
    ```

    Use the below `rsync` command if you use SSH keys to log in to your source server.

    ```bash
    rsync -avz -e "ssh -i ssh-key-file"  user@remote_server:~/odk-backups  ~
    ```

    **`-a`** : Archive mode, which preserves file attributes and permissions.

    **`-v`** : Verbose output, so you can see the progress.

    **`-z`** : Compresses data during transfer, reducing the network bandwidth used.

    **`-e "ssh -i ssh-key-file"`** : Tells `rsync` to use SSH for connecting to the remote server using  `-i ssh-key-file` SSH private key file for authentication.

    **`user@remote_server:~/odk-backups/`** : **`user`** is the username for the remote server while   **`remote_server`** is the IP address or registered hostname for the remote server. _odk-backups_ refers to the folder containing the backup filles on the remote server.

    Replace **`user`**, **`remote_server`**, and **`ssh-key-file`**  with actual values for your environment.

A message and prompt similar to the one below may be displayed if SSH does not have a record of the remote server's public key. Make sure you trust the remote server before typing ‘yes’ to continue connecting.

  ```bash
  The authenticity of host 'kagundajm.codes (93.184.216.34)' can't be established.
  ED25519 key fingerprint is SHA256:7dvpverJGLBDWqd8uxsLX9kWgBiZnYcXL07B+w3eIds.
  This key is not known by any other names
  Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
  ```

## Restore ODK Central Direct Backup File

The first time ODK Central runs, it creates the _/data/transfer_ folder on the host server and on the **`central-service-1`** Docker container. The purpose of the folder is to allow exchange of data between the host server and the Docker container. Any changes made to _/data/transfer_ in the **`central-service-1`** container will be reflected in _/data/transfer_ on your host server, and vice versa. To restore the Direct Backup file, you require to place the file in this folder.

1. Log in to your ODK Central destination server if you have logged out.

1. Move the Direct Backup file to the  _/data/transfer_  folder using the command below. Replace **`{backup-filename.zip}`** with the actual name that you used during the backup process.

    ```bash
    mv ~/odk-backups/{backup-filename.zip} /data/transfer/
    ```

    If you encounter a **"cannot create regular file: Permission denied"** error, prefix the command with **`sudo`**.

    ```bash
    sudo mv ~/odk-backups/{backup-filename.zip} /data/transfer/
    ```

1. Change directory to ODK Central installation folder by running the following commands.

    ```bash
    cd && cd central
    ```

1. Before running the command below, be aware that the command will replace all data on the destination server with the backup snapshot data. Once you are certain you want to replace the existing data, restore the backup using the following command:

    ```bash
    docker compose exec service node /usr/odk/lib/bin/restore.js /data/transfer/{backup-filename.zip} { backup-password}
    ```

    The command tells Docker to go into the running container named **`service`** and execute a Node.js script (_restore.js_) which expects a backup file (_backup-filename.zip_) and backup password (backup-password).

    Replace **`backup-filename.zip`** with the file name you moved to the _/data/transfer/_ folder and **`{backup-password}`** with the password used during the backup process.

    If you did not set a backup password during the backup process, press **ENTER** key immediately after typing the backup filename. Avoid trailing spaces after the filename **`backup-filename.zip`**.

    ```bash
    docker compose exec service node /usr/odk/lib/bin/restore.js /data/transfer/{backup-filename.zip}
    ```

    If the server displays "**no configuration file provided: not found**", you are running the command outside of ODK Central installation folder.

1. Wait for the backup restoration process to complete. The duration of your wait time will depend on the size of your backup file. After the restore process runs to completion, a success message including tasks that you require to revisit and verify will be displayed.

    ```bash
    kagunda:~$
    kagunda:~$ cd && cd central
    kagunda:~/central$ docker compose exec service node /usr/odk/lib/bin/restore.js /data/transfer/odk-bk20230913-08AM.zip bk202309
    Success. You will have to log out of the site and log back in.
        IMPORTANT: EVERYTHING has been restored to the way things were at the time of backup, including:
        * all passwords and email addresses.
        * anything deleted since the backup was made now exists again.
        * your backup settings.
        Please revisit all of these and make sure they are okay.
    '{"success":true}'
    kagunda:~/central$
    ```

## Restore Enketo Redis Data and Secrets

1. Make sure you are stilled logged in to the destination server and you are working from the ODK Central installation  folder.

1. Copy the main Enketo Redis database (_enketo-main.rdb_) to the Docker container using the following commands:

    ```bash
    docker stop central-enketo_redis_main-1
    docker cp ~/odk-backups/enketo-main.rdb central-enketo_redis_main-1:/data/enketo-main.rdb;
    docker start central-enketo_redis_main-1;
    ```

    Example commands and output:

    ```bash
    kagunda:~/central$
    kagunda:~/central$ docker stop central-enketo_redis_main-1
    central-enketo_redis_main-1
    kagunda:~/central$ docker cp ~/odk-backups/enketo-main.rdb central-enketo_redis_main-1:/data/enketo-main.rdb;
    Successfully copied 39.9kB to central-enketo_redis_main-1:/data/enketo-main.rdb
    kagunda:~/central$
    kagunda:~/central$ docker start central-enketo_redis_main-1;
    central-enketo_redis_main-1
    kagunda:~/central$
    ```

1. Copy the Enketo Redis cache database (_enketo-cache.rdb_) to the Docker container:

    ```bash
    docker stop central-enketo_redis_cache-1
    docker cp ~/odk-backups/enketo-cache.rdb central-enketo_redis_cache-1:/data/enketo-cache.rdb;
    docker start central-enketo_redis_cache-1;
    ```

1. Copy the _secrets_ folder to the **`central-service-1`** Docker container:

    ```bash
    docker stop central-service-1
    docker cp ~/odk-backups/secrets central-service-1:/etc;
    docker start central-service-1;
    ```

## Update Destination Server Enketo Config File.

1. Copy the config keys text file to the _config_ folder in the Enketo Docker container.

    ```bash
    docker cp  ~/odk-backups/enketo-config-keys.txt central-enketo-1:/srv/src/enketo_express/config/
    ```

1. Open a Bash shell in the Enketo Docker container.

      ```bash
      docker exec -it central-enketo-1 /bin/bash
      ```

1. Install **nano** editor.

    ```bash
    apt-get install nano -y
    ```

    **`-y`** option will assume "yes" as the answer to any prompts that may occur during the installation process.

1. Open the Enketo config file with the **nano** editor.

    ```bash
    nano config/config.json;
    ```

1. Prefix "encryption key", "less secure encryption key", and "api key" keys with **#** (or any other character). This will help you differentiate between the existing keys from the new keys when updating.

      ```json
      {
          "app name": "Enketo",
          "base path": "-",
          #"encryption key": "contents of enketo-secret",
          "id length": 31,
          #"less secure encryption key": "contents of enketo-less-secret",
          "linked form and data server": {
              #"api key": "contents of enketo-api-key",
              ...
          },
          ...
      }
      ```

1. Move to the beginning of the file and press the **ENTER** key after the opening curly brace (**{**) to create a blank line.

1. Press **Control + R** (**^R**) keys. The editor will display a **"File to insert [from ./]:"** prompt. Type _config/enketo-config-keys.txt_ next to the prompt and press the **ENTER** key to insert the contents of the file at the cursor location.

1. Move the cursor to the "encryption key" line of the data that has been inserted to the file.

1. Press  **Control** + **K** (**^K**) keys to cut the line.

1. Move the cursor to the beginning of "encryption key" prefixed with a **#** character.

1. Press  **Control** + **U** (**^U**) keys to paste.

1. Repeat the previous three steps to update "less secure encryption key" and "api key"  values.

1. Remove the lines prefixed with **#** by moving to each line and pressing **Control** + **K** (**^K**).

1. Save the changes and exit the **nano** editor.

1. Installation of **nano** editor was temporarily. Therefore, uninstall the editor and exit the Enketo container using the following commands.

    ```bash
    apt-get remove nano -y;
    exit;
    ```

1. Restart the Enketo Docker container to effect the changes in the configuration file.

    ```bash
    docker restart central-enketo-1
    ```

1. Optionally show the active status of the docker containers.

    ```bash
    docker ps --format 'table {{.Names}}\t{{.State}}'
    ```

## Change Domain Names

If the ODK Central domain names on the source and destination servers are different, you will encounter the following error when you attempt to preview restored forms on the destination server.

  >> This form does not exist or you no longer have access to it. Please check the URL for any missing characters.
  >> If the form existed previously, it may have been archived, disabled or deleted. If this is unexpected, please contact the person who asked you to fill the form.
  >> (Attempted to access form with ID: )

![ODK Central form does not exist error.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yt1c2xm9ii2eouvc7l5q.png)


To resolve the error, you must replace the old domain values with the new domain values in the Redis databases.

### Create Domain Update Script Files

1. On the destination server, create _~/main-odk-domains.sh_ script file.

    ```bash
    nano ~/main-odk-domains.sh
    ```

1. Update the file with the following:

    ```sh
    #!/bin/sh

    OLD_DOMAIN="old-domain.com"
    NEW_DOMAIN="new-domain.com"

    keys=$(redis-cli -p 6379 KEYS  "*")

    for key in $keys
    do
      if [ "$(redis-cli -p 6379 type "$key")" = "string" ]; then
        if echo "$key" | grep -q "$OLD_DOMAIN"; then
          new_key=$(echo "$key" | sed "s/$OLD_DOMAIN/$NEW_DOMAIN/g")
          redis-cli -p 6379  RENAME "$key" "$new_key"
        fi
      elif [ "$(redis-cli -p 6379 type "$key")" = "hash" ]; then
        existingRosaServer=$(redis-cli -p 6379  HGET "$key" "openRosaServer")
        if echo "$existingRosaServer" | grep -q "$OLD_DOMAIN"; then
          newRosaServer=$(echo "$existingRosaServer" | sed "s/$OLD_DOMAIN/$NEW_DOMAIN/g")
          redis-cli -p 6379 HSET "$key" "openRosaServer" "$newRosaServer"
        fi

        if echo "$key" | grep -q "$OLD_DOMAIN"; then
          new_key=$(echo "$key" | sed "s/$OLD_DOMAIN/$NEW_DOMAIN/g")
          redis-cli -p 6379  RENAME "$key" "$new_key"
        fi
      fi
    done
    ```

    The script fetches all keys from the Redis database and iterates over each key. For each key, it substitutes any occurrences of old domain values with the new domain values and renames the key in the database. Where the type of key is **hash**, the value of the **openRosaServer** field is updated to the new domain.

    The **sed** command is used to substitute old domain values with new domain values. The **`g`** at the end of the command tells **sed** to replace all occurrences of OLD_DOMAIN in the key.

    **`grep -q`** command searches for old domain value matches within the key and exits with either a success or failure. The **`-q`** option suppresses display of the output.

1. Save the file and exit the **nano** editor.

1. Make the script file executable

    ```bash
    chmod +x  ~/main-odk-domains.sh
    ```

1. Copy _main-odk-domains.sh_ to _cache-odk-domains.sh_

    ```bash
    cp ~/main-odk-domains.sh ~/cache-odk-domains.sh
    ```

1. Open _cache-odk-domains.sh_ with a text editor and replace any occurrences of **`redis-cli -p 6379`** with  **`redis-cli -p 6380`**.

1. Save the changes and exit the editor.

### Update Enketo Redis Main Database

1. Copy the _main-odk-domains.sh_ script file to the main Enketo Redis database Docker container.

    ```bash
    docker cp ~/main-odk-domains.sh central-enketo_redis_main-1:/root
    ```

1. Open a Bourne shell in the main Enketo Redis database container.

      ```bash
      docker exec -it central-enketo_redis_main-1 sh
      ```

1. Run the script file

    ```bash
    /root/main-odk-domains.sh
    ```

1. Optionally, confirm the update has been successful

    ```bash
    redis-cli -p 6379 KEYS "*NEW_DOMAIN*"
    ```

1. Exit the Docker container

    ```bash
    exit
    ```

### Update Enketo Redis Cache Database

1. Copy the _cache-odk-domains.sh_ script file to the cache Enketo Redis database Docker container.

    ```bash
     docker cp ~/cache-odk-domains.sh central-enketo_redis_cache-1:/root
    ```

1. Open a Bourne shell in the cache Enketo Redis database container

      ```bash
      docker exec -it central-enketo_redis_cache-1 sh
      ```

1. Run the script file

    ```bash
    /root/cache-odk-domains.sh
    ```

1. Exit the Docker container

    ```bash
    exit
    ```

## Conclusion

This tutorial has shown you how to backup and restore ODK Central data. You have learned how to create a direct backup of your ODK Central database, copy the backup file and other essential data to the destination server, and restore the backup file on the new server. You have also learned how to update the domain names in the Enketo Redis databases and config files to avoid errors when previewing forms on the new server.

If you found this article useful, please share it with your friends and colleagues who might benefit from it.

Thank you for reading!

## Resources

- [Central Backup Restore](https://forum.getodk.org/t/central-backup-restore/33047/10)

- [Restoring a backup](https://docs.getodk.org/central-backup/#restoring-a-backup)

- [Backup api call fails after several minutes with ERR_STREAM_PREMATURE_CLOSE](https://forum.getodk.org/t/backup-api-call-fails-after-several-minutes-with-err-stream-premature-close/40924/2)

- [Failed to restore ODK central using a downloaded backup file](https://forum.getodk.org/t/failed-to-restore-odk-central-using-a-downloaded-backup-file/40785/5)

- [Trying to migrate my ODK deployment and its data from AWS to Digital Ocean](https://forum.getodk.org/t/trying-to-migrate-my-odk-deployment-and-its-data-from-aws-to-digital-ocean/39404/2)

- [I’m running Central on Ubuntu and want to migrate to another server](https://forum.getodk.org/t/im-running-central-on-ubuntu-and-want-to-migrate-to-another-server/40212/2)