﻿---
path: faq/docker/fault-tolerance/migration-to-replica-set.mdx
title: Migrating from standalone to a replica set
slug: migration-to-replica-set
description: >-
  Configuring a MongoDB replica set when Passwork is already running in Docker:
  preparing the primary and secondary servers, the keyfile, rs0 initialization,
  and restoring from a backup.
keywords:
  - Passwork
  - Docker
  - Replica Set
  - MongoDB
  - fault tolerance
  - standalone
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

:::danger
MongoDB replication requires at least **three servers**.
:::

## Overview

An example of configuring a MongoDB replica set when Passwork is already installed and in use, but you need to move to a fault-tolerant setup.

The following host names are used as an example:

- First server (PRIMARY) — **example.docker.1**
- Second server (SECONDARY) — **example.docker.2**
- Third server (SECONDARY) — **example.docker.3**

## Preparing the primary server

:::danger
The steps below are performed on the server where Passwork is installed and in use — **example.docker.1**.
:::

Create a MongoDB database backup using the `./db-backup.sh` script:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
cd /<passwork>/
./db-backup.sh
```

  </TabItem>
</Tabs>

Stop and remove the `db` service:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
docker compose down db
```

  </TabItem>
</Tabs>

Clear the previously created MongoDB collections and data:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
rm -rf ./data/mongo/*
```

  </TabItem>
</Tabs>

Edit and extend the `.env` file. Insert the following lines and specify your own values:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
# Database user name used at initialization.
MONGO_INITDB_ROOT_USERNAME=username
# Database user password used at initialization.
MONGO_INITDB_ROOT_PASSWORD=password
# DNS name or IP address of the host where the database is installed
MONGO_NODE_NAME="example.docker.1"
# Flags the database is started with
DB_RUN_FLAGS="--replicaSet rs0 --keyFile=/data/keys/keyfile"
```

  </TabItem>
</Tabs>

## Preparing the secondary servers

:::danger
The steps below are performed on the **example.docker.2** and **example.docker.3** servers.
:::

### Installing Docker

1. Download and install Docker. The minimum required version is 18.06.0.  
   Official article: [installing Docker](https://docs.docker.com/engine/installation/).
2. Install and enable the Docker Compose plugin if it is not installed.  
   Official article: [installing Docker Compose (Linux)](https://docs.docker.com/compose/install/linux/).

:::warning
We recommend using a Docker installation that was **not** made through snap.
:::

### Installing Passwork

Create a directory for the Passwork installation and change into it:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
mkdir /<passwork>/ && cd /<passwork>/
```

  </TabItem>
</Tabs>

Download the docker compose build archive and extract it:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
curl -SfL https://repos.passwork.pro/repository/passwork-docker/passwork_compose_last.tar.gz | tar xzvf - 2>&1
rm -f passwork_compose_last.tar.gz
```

  </TabItem>
</Tabs>

Rename the environment variables configuration file:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
mv ./.env.example ./.env
```

  </TabItem>
</Tabs>

Edit the `.env` file:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
vim ./.env
```

  </TabItem>
</Tabs>

- Put the API key from the [Customer Portal](https://portal.passwork.pro) into the `PORTAL_KEY` line:

<ImageComponent
  src={'/img/assets/faq-docker-migration-to-replica-set-apikey.png'}
  alt="The API key in the Customer Portal"
  format='regular'
/>

- At the end of the variables file, insert the following lines and specify your own values:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
# Database user name used at initialization.
MONGO_INITDB_ROOT_USERNAME=username
# Database user password used at initialization.
MONGO_INITDB_ROOT_PASSWORD=password
# DNS name or IP address of the host where the database is installed
MONGO_NODE_NAME="example.docker.2(3)"
# Flags the database is started with
DB_RUN_FLAGS="--replicaSet rs0 --keyFile=/data/keys/keyfile"
```

  </TabItem>
</Tabs>

Run the script that downloads the Passwork code and places it into the Docker build:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
cd /<passwork>/ && ./update.sh
```

  </TabItem>
</Tabs>

## Preparing the environment

:::danger
The steps below are performed on **every** server: example.docker.1, example.docker.2, example.docker.3.
:::

Edit the Docker container configuration file `/<passwork>/docker-compose.yaml`:

- Uncomment the `ports` parameter and its value in the `db` service:

```yaml
ports:
  - 27017:27017
```

:::info
Forwarding the port from the container to the host is required for access and data exchange between the database instances.
:::

### Generating and configuring the replica set key

On **example.docker.1**, generate the key file for the replica set:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
openssl rand -base64 756 > /<passwork>/conf/mongo/keyfile
```

  </TabItem>
</Tabs>

:::warning
On the secondary servers, create the `mongo` directory: `mkdir -p /<passwork>/conf/mongo`.
:::

The key file is used inside the replica set to authorize the nodes. If the file is not specified or its permissions are broader than **0400**, the replica set will not assemble correctly.

Copy the generated key to the secondary servers using `scp`:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
scp /<passwork>/conf/mongo/keyfile username@example.docker.2:/<passwork>/conf/mongo/
scp /<passwork>/conf/mongo/keyfile username@example.docker.3:/<passwork>/conf/mongo/
```

  </TabItem>
</Tabs>

Set the permissions for the key file and the directory on **every** server:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
chown 1001:1001 /<passwork>/conf/mongo/keyfile
chown 1001:1001 /<passwork>/conf/mongo/
chmod 0700 /<passwork>/conf/mongo/
chmod 0400 /<passwork>/conf/mongo/keyfile
```

  </TabItem>
</Tabs>

## Configuring the replica set

### Starting the containers

:::warning
If DNS is not configured on the servers, add the records to `/etc/hosts` on every node:
- 172.16.X.X example.docker.1
- 172.16.X.X example.docker.2
- 172.16.X.X example.docker.3
:::

Start the Docker containers:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
# Primary server
cd /<passwork>/
docker compose up -d --force-recreate db
# Secondary servers
cd /<passwork>/
docker compose up -d
```

  </TabItem>
</Tabs>

Connect to the MongoDB shell on **example.docker.1**:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
docker exec -it passwork_db mongosh -u username -p password
```

  </TabItem>
</Tabs>

Switch to the `admin` database:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
use admin
```

  </TabItem>
</Tabs>

Initialize the replica set named `rs0`:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
rs.initiate( {
   _id : "rs0",
   members: [
      { _id: 0, host: "example.docker.1:27017" },
      { _id: 1, host: "example.docker.2:27017" },
      { _id: 2, host: "example.docker.3:27017" }
   ]
})
```

  </TabItem>
</Tabs>

Check the status:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
rs.status()
```

  </TabItem>
</Tabs>

### Restoring the database from a backup

On **example.docker.1**, change into the installation directory and start the restore:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
cd /<passwork>/
./db-restore.sh
```

  </TabItem>
</Tabs>

Edit the `/<passwork>/conf/keys/config.env` configuration file and specify the parameters of the configured replica set:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
MONGODB_URL=mongodb://example.docker.1:27017,example.docker.2:27017,example.docker.3:27017/?replicaSet=rs0
MONGODB_DB=pw
MONGODB_USERNAME=username
MONGODB_PASSWORD=password
```

  </TabItem>
</Tabs>

Connect to the Passwork web interface on `example.docker.1` and verify that the MongoDB replica set is working.

### Configuring the secondary server connections

:::danger
These steps are performed on the **secondary** servers.
:::

Copy the generated configuration file `config.env` and the `encryption_key` from `example.docker.1` to the secondary servers:

<Tabs className="tabs-container">
  <TabItem className="tab-item-container" value="shell" label="shell">

```bash
scp /<passwork>/conf/keys/config.env username@example.docker.2:/<passwork>/conf/keys/
scp /<passwork>/conf/keys/config.env username@example.docker.3:/<passwork>/conf/keys/
scp /<passwork>/conf/keys/encryption_key username@example.docker.2:/<passwork>/conf/keys/
scp /<passwork>/conf/keys/encryption_key username@example.docker.3:/<passwork>/conf/keys/
```

  </TabItem>
</Tabs>

On all three nodes, edit the `APP_URL` parameter in the configuration and specify the DNS name or IP address of the server.
