Master-Slave PostgreSQL Replication Setup on Debian 12

PostgreSQL replication is a robust solution for ensuring database availability, load balancing, and failover readiness. This guide provides a step-by-step approach to setting up Master-Slave replication on Debian 12, using PostgreSQL 15, along with hostname-based configurations.

1. System Prerequisites

1.1 Server Environment

We will use four Debian 12 servers with PostgreSQL 15 installed:

  • Master: postgre.master.x1.demo.internal (10.11.33.53)
  • Slave 1: postgre.slave.x1.demo.internal (10.11.33.66)
  • Slave 2: postgre.slave.x2.demo.internal (10.11.33.67)
  • Slave 3: postgre.slave.x3.demo.internal (10.11.33.68)

1.2 Update Hostname Configuration

Ensure that your /etc/hosts file includes the following lines:

127.0.0.1   localhost
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.11.33.53 postgre.master.x1.demo.internal
10.11.33.66 postgre.slave.x1.demo.internal
10.11.33.67 postgre.slave.x2.demo.internal
10.11.33.68 postgre.slave.x3.demo.internal
Code language: CSS (css)

2. Install PostgreSQL on All Servers

sudo apt update
sudo apt install -y postgresql postgresql-contrib
sudo apt install -y postgresql-15-postgis-3 postgresql-15-postgis-3-scripts


3. Configure the Master Server

3.1 Enable Replication Settings

Modify the PostgreSQL configuration file on the master:

sudo vi /etc/postgresql/15/main/postgresql.conf

Ensure these settings are applied:

listen_addresses = '*'
wal_level = replica
max_wal_senders = 10
wal_keep_size = 512MB
max_wal_size = 4GB
min_wal_size = 1GB
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/15/archive/%f'
Code language: JavaScript (javascript)

Create the archive directory:

mkdir -p /var/lib/postgresql/15/archive
chown -R postgres:postgres /var/lib/postgresql/15/archive
chmod 700 /var/lib/postgresql/15/archive
Code language: JavaScript (javascript)

3.2 Configure Authentication

Modify the pg_hba.conf file to allow replication:

nano /etc/postgresql/15/main/pg_hba.conf

Add these lines:

# --- keep your existing local/loopback lines above ---

# 1) Replication: allow only the replicator from your subnet (MD5 as required)
host    replication     replicator      10.11.0.0/16           md5

# 2) App/client access: open to all (temporarily) but prefer SCRAM
host    all             all             0.0.0.0/0              scram-sha-256
host    all             all             ::/0                   scram-sha-256

# 3) Optional hardening even while “open”:
#    prevent remote superuser logins
host    all             postgres        0.0.0.0/0              reject
host    all             postgres        ::/0                   reject

Code language: PHP (php)

3.3 Restart the PostgreSQL Service

systemctl restart postgresql

3.4 Create Replication User

Switch to the PostgreSQL user and execute:

su - postgres -c "psql -c \"CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'yourpassword';\""
Code language: JavaScript (javascript)


4. Configure the Slave Servers

4.1 Stop PostgreSQL on the Slaves

systemctl stop postgresql

4.2 Remove Existing Data

rm -rf /var/lib/postgresql/15/main/*
Code language: JavaScript (javascript)

4.3 Synchronize Data from Master

su - postgres -c "pg_basebackup -h postgre.master.x1.demo.internal -D /var/lib/postgresql/15/main -U replicator -Fp -Xs -P -R"
Code language: JavaScript (javascript)

4.4 Create Standby Signal (If Needed)

touch /var/lib/postgresql/15/main/standby.signal
Code language: JavaScript (javascript)

4.5 Configure Connection to Master

Edit postgresql.conf on each slave:

nano /etc/postgresql/15/main/postgresql.conf
primary_conninfo = 'host=postgre.master.x1.demo.internal port=5432 user=replicator password=yourpassword'
Code language: JavaScript (javascript)

4.6 Restart PostgreSQL on the Slaves

systemctl start postgresql


5. Verify Replication

5.1 Check Master Replication Status

On the master, run:

su - postgres -c "psql -c \"SELECT * FROM pg_stat_replication;\""
Code language: JavaScript (javascript)

5.2 Check Slave Replication Status

On each slave, run:

su - postgres -c "psql -c \"SELECT pg_is_in_recovery();\""
Code language: JavaScript (javascript)

If replication is active, the output should be true.

5.3 Test Replication

On the master, create a test database and table:

su - postgres -c "psql -c \"CREATE DATABASE replication_test;\""
su - postgres -c "psql -d replication_test -c \"CREATE TABLE sample_table (id SERIAL PRIMARY KEY, value TEXT);\""
su - postgres -c "psql -d replication_test -c \"INSERT INTO sample_table (value) VALUES ('Replication Works!');\""
Code language: JavaScript (javascript)

On each slave, verify the data exists:

su - postgres -c "psql -d replication_test -c \"SELECT * FROM sample_table;\""
Code language: JavaScript (javascript)

6. Monitoring & Maintenance

6.1 Monitor Logs

journalctl -u postgresql

6.2 Set Up Automated Failover (Optional)

Consider using pg_auto_failover or Patroni for automated failover.

6.3 Schedule Regular Backups

Even with replication, backups are necessary. Use:

su - postgres -c "pg_dump -h postgre.master.x1.demo.internal -F c -b -v -f /backup/pg_backup.dump mydatabase"
Code language: JavaScript (javascript)

6.4 Automate WAL Cleanup

To prevent excessive storage usage, set up a cron job to delete old WAL files:

crontab -e

Add the following line to remove WAL files older than 7 days:

0 3 * * * sudo find /var/lib/postgresql/15/archive -type f -mtime +7 -delete
Code language: JavaScript (javascript)

This will run daily at 3 AM to free up space while ensuring replication stability.

7. Summary

This guide provides a host-based PostgreSQL Master-Slave replication setup on Debian 12. With real-time data replication, it ensures high availability and disaster recovery. By following these steps, your database cluster is resilient, scalable, and ready for production workloads. 🚀

PostgreSQL Storage Paths to Consider for Mounting on Debian 12

When setting up a PostgreSQL master-slave replication or general database storage management, you should consider mounting specific storage paths to optimize performance, ensure data integrity, and manage disk usage efficiently. Below is a list of important PostgreSQL storage paths and their purposes:

1. Data Directory (Main Database Storage)

Path: /var/lib/postgresql/<version>/main/
Purpose: Stores all PostgreSQL database files, including tables, indexes, and internal metadata.
Mount Recommendation: High-performance SSD with enough capacity for database growth.
If you have 30 GB allocated for database storage, ensure at least 50% headroom for future expansion, making a total of 45 GB recommended.

2. WAL (Write-Ahead Logging) Files

Path: /var/lib/postgresql/<version>/main/pg_wal/
Purpose: Stores transaction logs for crash recovery and replication.
Mount Recommendation: Fast SSD or NVMe disk with high IOPS to improve database write performance.
PostgreSQL retains WAL logs based on wal_keep_size or max_wal_size, which can vary. For a moderate workload, 10-15 GB recommended.

3. Replication Slots (if used for logical replication)

Path: /var/lib/postgresql/<version>/main/pg_replslot/
Purpose: Stores replication slots for logical replication.
Mount Recommendation: SSD or same disk as WAL to maintain replication efficiency.
Each replication slot can accumulate WAL logs if the replica lags behind, so allocate 5-10 GB recommended.

4. Temporary Files (Sort & Query Execution)

Path: /var/lib/postgresql/<version>/main/base/pgsql_tmp/
Purpose: Stores temporary files created during query execution and sorting operations.
Mount Recommendation: Separate fast disk (optional) if handling large temporary data.
For high-performance queries, 5-10 GB recommended.

5. Log Files

Path: /var/log/postgresql/
Purpose: Stores PostgreSQL logs, including query logs, errors, and slow query logs.
Mount Recommendation: Separate disk or partition for better log management and prevent logs from filling up system storage.
Log rotation should be configured properly. 3-5 GB recommended.

6. Archive Logs (for Point-in-Time Recovery – PITR, if enabled)

Path: /var/lib/postgresql/<version>/archive
Purpose: Stores archived WAL files for backups and point-in-time recovery.
Mount Recommendation: Separate storage or remote storage (NFS, object storage, or backup server). This depends on your backup retention policy.
10-20 GB recommended.

7. Configuration Files

Path: /etc/postgresql/<version>/main/
Purpose: Stores PostgreSQL configuration files (postgresql.conf, pg_hba.conf, pg_ident.conf).
Mount Recommendation: Typically left on the system disk but can be backed up separately.
Minimal storage required, <1 GB sufficient.

8. Tablespaces (if using custom storage for specific tables/indexes)

Path: Custom path defined when creating a tablespace.
Purpose: Used for storing specific tables and indexes on different storage devices.
Mount Recommendation: Dedicated high-performance storage for large tables or indexes.
If large datasets require separate storage, allocate based on table sizes, 20-50 GB recommended.

9. Backup Storage (Optional)

Path: /mnt/backup_postgres/
Purpose: A separate mounted disk or network storage for scheduled database backups.
Mount Recommendation: External storage or remote backup server.
This should be sized based on your backup retention policy, at least 100 GB recommended.

Recommended Mount Strategy

Storage PathRecommended Mount TypePurposeEstimated Disk Size
/var/lib/postgresql/<version>/main/High-performance SSD/NVMeMain database storage45 GB
/var/lib/postgresql/<version>/main/pg_wal/SSD/NVMeWrite-Ahead Logs (WAL)10-15 GB
/var/lib/postgresql/<version>/main/pg_replslot/SSD/NVMeReplication slots5-10 GB
/var/lib/postgresql/<version>/main/base/pgsql_tmp/SSD/NVMe (if needed)Temporary query storage5-10 GB
/var/log/postgresql/Separate disk or partitionLogs storage3-5 GB
/var/lib/postgresql/<version>/archive/Remote/NFS or separate diskWAL archive for PITR10-20 GB
/mnt/backup_postgres/Remote or external storageBackup location100 GB+

For a master-slave setup, ensure that WAL and replication slots have adequate storage to prevent replication lag or failures. If your replication lag is high, increase the allocated space for WAL to avoid losing data during recovery.

Let me know if you need further customizations for your specific setup!

How to Set Up WSL on Windows 11 and Transfer Debian 12 Between Devices with a Custom Name

With the Windows Subsystem for Linux (WSL), you can enjoy the power of Linux on your Windows 11 system. This guide will walk you through setting up WSL, running Debian 12, exporting it from a desktop computer, and importing it to a laptop with a custom name as the second Debian 12 instance.

1. Setting Up WSL on Windows 11

Enable WSL on Windows 11, Open PowerShell as Administrator and run:

wsl --install

This command enables WSL, installs the latest Linux kernel, and sets up a default Linux distribution. Restart your computer if prompted.

Install Debian 12, Open the Microsoft Store and search for “Debian”. Click on Debian 12 and install it. Launch Debian 12 and complete the initial setup by creating a username and password.

Verify WSL Installation, Open a PowerShell terminal and run:

wsl --list --verbose

Ensure that Debian 12 appears in the list with the state “Running” or “Stopped”.

2. Exporting Debian 12 from Your Desktop

Open PowerShell as Administrator: Run the following command to export your Debian 12 instance to a .tar file:

wsl --export Debian-12 C:\path\to\backup\debian12.tar

Replace C:\path\to\backup\debian12.tar with your desired file path.

Verify the Export, navigate to the specified path and ensure that the .tar file is present.

Transfer the File to Your Laptop, use a USB drive, network share, or a cloud service to transfer the .tar file to your laptop.

3. Importing Debian 12 to Your Laptop with a Custom Name

Copy the .tar File, place the .tar file in a directory on your laptop, e.g., C:\Users\YourName\Documents\Debian12.tar.

Run the Import Command,, Open PowerShell as Administrator and run:

wsl --import MyCustomDebian C:\WSL\MyCustomDebian C:\Users\YourName\Documents\Debian12.tar

Replace MyCustomDebian with your preferred name and adjust the paths as needed. This will create a new WSL instance named “MyCustomDebian”.

Verify the Import, check if the new instance is imported successfully:

wsl --list --verbose

Ensure “MyCustomDebian” appears in the list.

Run Your Custom Debian Instance: Start the new instance by running:

wsl -d MyCustomDebian

4. Testing and Customizing the Setup

Test Both Instances, If you have another Debian 12 instance on your laptop, ensure they work independently by running each with:

wsl -d <InstanceName>

Set Custom Configuration (optional), Update configuration files specific to the new instance, such as .bashrc or .profile.

FInal Words

By following these steps, you’ve successfully set up WSL on Windows 11, exported Debian 12 from your desktop, and imported it to your laptop with a custom name.

This setup allows you to maintain multiple Linux environments tailored to your needs, making your workflow more flexible and efficient.

Installing and Setting Up RabbitMQ Master-Slave Cluster on Debian 12

RabbitMQ is a robust message broker widely used in distributed systems. This guide provides a detailed, step-by-step tutorial on installing RabbitMQ 3.10.8 with Erlang 25.2.3 on Debian 12 and configuring a Master-Slave cluster. The setup will involve two servers: a Master at 10.11.33.83 and a Slave at 10.11.33.93.

1. Configure Hostname Resolution

Ensure both servers can resolve each other’s hostnames by editing /etc/hosts.

On both Master and Slave servers, add the following lines to /etc/hosts:

127.0.0.1   localhost

# IPv6 settings
::1         localhost ip6-localhost ip6-loopback
ff02::1     ip6-allnodes
ff02::2     ip6-allrouters

# RabbitMQ Cluster Nodes
10.11.33.83 rabbit.master.x1.demo.internal
10.11.33.93 rabbit.slave.x1.demo.internal
Code language: CSS (css)

Save and exit the file.

Verify the hostname resolution by running:

ping -c 3 rabbit.master.x1.demo.internal
ping -c 3 rabbit.slave.x1.demo.internal
Code language: CSS (css)

2. Install Required Dependencies

Update the package list and install necessary dependencies:

sudo apt update && sudo apt install -y curl gnupg apt-transport-https

3. Install Erlang 25.2.3

RabbitMQ requires Erlang. Install version 25.2.3 by adding the RabbitMQ package repository.

curl -fsSL https://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc | sudo tee /usr/share/keyrings/erlang.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/erlang.gpg] https://packages.erlang-solutions.com/debian $(lsb_release -cs) contrib" | sudo tee /etc/apt/sources.list.d/erlang.list
sudo apt update
sudo apt install -y erlang=1:25.2.3-1
Code language: PHP (php)

update 2025, 14th May. the above installation maybe broken, you can install directly from Debian 12 repo.

sudo apt update
sudo apt install erlang

Verify installation:

erl -version

4. Install RabbitMQ 3.10.8

Add the RabbitMQ repository:

curl -fsSL https://packagecloud.io/rabbitmq/rabbitmq-server/gpgkey | sudo tee /usr/share/keyrings/rabbitmq-keyring.asc > /dev/null
echo "deb [signed-by=/usr/share/keyrings/rabbitmq-keyring.asc] https://packagecloud.io/rabbitmq/rabbitmq-server/debian/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/rabbitmq.list
sudo apt update
sudo apt install -y rabbitmq-server=3.10.8-1
Code language: PHP (php)

update 2025, 14th May. the above installation maybe broken, you can install directly from Debian 12 repo.

sudo rm /etc/apt/sources.list.d/rabbitmq.list
sudo apt update
sudo apt install rabbitmq-serverCode language: PHP (php)

Enable and start RabbitMQ:

sudo systemctl enable --now rabbitmq-server

Check RabbitMQ status:

sudo systemctl status rabbitmq-server

5. Configure RabbitMQ Environment

Set RabbitMQ to use long hostnames and define a fixed node name.

On Master (10.11.33.83):

sudo nano /etc/rabbitmq/rabbitmq-env.conf

Add:

[email protected]
RABBITMQ_USE_LONGNAME=true
Code language: JavaScript (javascript)

Save and exit.

On Slave (10.11.33.93):

sudo nano /etc/rabbitmq/rabbitmq-env.conf

Add:

[email protected]
RABBITMQ_USE_LONGNAME=true
Code language: JavaScript (javascript)

Save and exit.

Restart RabbitMQ on both servers:

sudo systemctl restart rabbitmq-server

Verify the node name:

sudo rabbitmqctl status

6. Set Erlang Cookie for Clustering

Ensure the Erlang cookie is the same on both servers:

echo "SUPER_SECRET_COOKIE_VALUE" | sudo tee /var/lib/rabbitmq/.erlang.cookie
sudo chmod 600 /var/lib/rabbitmq/.erlang.cookie
sudo chown rabbitmq:rabbitmq /var/lib/rabbitmq/.erlang.cookie
Code language: PHP (php)

Restart RabbitMQ:

sudo systemctl restart rabbitmq-server

7. Enable RabbitMQ Management Plugin

Enable the RabbitMQ management plugin to monitor the cluster:

sudo rabbitmq-plugins enable rabbitmq_management
sudo systemctl restart rabbitmq-server

Access the RabbitMQ web UI at:

http://<server-ip>:15672

Default credentials:

  • Username: guest
  • Password: guest

8. Configure Cluster (Master-Slave)

Ensure RabbitMQ is running on the Slave before attempting to stop the application:

sudo systemctl start rabbitmq-server

Stop RabbitMQ application on the Slave server:

sudo rabbitmqctl stop_app

Join the Slave node to the Master:

sudo rabbitmqctl join_cluster rabbit@rabbit.master.x1.demo.internal
sudo rabbitmqctl start_app
Code language: CSS (css)

Check the cluster status:

sudo rabbitmqctl cluster_status

9. Configure RabbitMQ Cluster in /etc/rabbitmq/rabbitmq.conf

By default, RabbitMQ does not create /etc/rabbitmq/rabbitmq.conf, so you must create it manually:

sudo nano /etc/rabbitmq/rabbitmq.conf

Add the following lines to define the cluster settings:

cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config
cluster_formation.classic_config.nodes.1 = [email protected]
cluster_formation.classic_config.nodes.2 = [email protected]

Save the file and restart RabbitMQ on both servers:

sudo systemctl restart rabbitmq-server

Verify the configuration:

sudo rabbitmqctl cluster_status

10. Set a High Availability Policy

On the Master node, apply an HA policy:

sudo rabbitmqctl set_policy ha-all ".*" '{"ha-mode":"all","ha-sync-mode":"automatic"}' --priority 0
Code language: JavaScript (javascript)

Verify policies:

sudo rabbitmqctl list_policies

11. Test the Cluster Setup

On Master, create a test queue:

sudo rabbitadmin -u guest -p guest declare queue name=test-ha durable=true
Code language: PHP (php)

Check queues on both Master and Slave:

sudo rabbitmqctl list_queues

12. Simulate Failover

To test failover, stop RabbitMQ on the Master:

sudo systemctl stop rabbitmq-server

Publish a message from the Slave:

sudo rabbitmqadmin -u guest -p guest publish routing_key=test-ha payload="Hello, RabbitMQ!"
Code language: JavaScript (javascript)

Restart the Master and verify the message persists:

sudo systemctl start rabbitmq-server
rabbitmqctl list_queues

Understanding Docker Connectivity, Networking and Configurations.

When working with Docker to build scalable applications, networking and service configuration are critical components. Over the course of our exploration, we’ve touched on a variety of topics, starting with creating a docker-compose.yaml template, understanding network modes like bridge and host, and configuring services like MySQL.

This article captures the complete journey to demystify Docker’s networking capabilities and service interconnectivity.

Creating a Docker Compose Template A docker-compose.yaml file is the backbone of a multi-container Docker application. It allows you to define and orchestrate services in one place. Here is an example of comprehensive docker template:

services:
  app:
    image: ${APP_IMAGE}
    container_name: ${APP_CONTAINER_NAME}
    restart: ${RESTART_POLICY}
    environment:
      - APP_ENV=${APP_ENV}
      - APP_PORT=${APP_PORT}
      - DATABASE_HOST=${DATABASE_HOST}
      - DATABASE_PORT=${DATABASE_PORT}
      - DATABASE_USER=${DATABASE_USER}
      - DATABASE_PASSWORD=${DATABASE_PASSWORD}
      - DATABASE_NAME=${DATABASE_NAME}
    ports:
      - "${HOST_PORT}:${APP_PORT}"
    volumes:
      - ${APP_VOLUME_SOURCE}:${APP_VOLUME_TARGET}
    networks:
      - ${NETWORK_NAME}
  db:
    image: ${DB_IMAGE}
    container_name: ${DB_CONTAINER_NAME}
    restart: ${RESTART_POLICY}
    environment:
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_DB=${DB_NAME}
    ports:
      - "${DB_HOST_PORT}:${DB_PORT}"
    volumes:
      - ${DB_VOLUME_SOURCE}:${DB_VOLUME_TARGET}
    networks:
      - ${NETWORK_NAME}
  redis:
    image: ${REDIS_IMAGE}
    container_name: ${REDIS_CONTAINER_NAME}
    restart: ${RESTART_POLICY}
    ports:
      - "${REDIS_HOST_PORT}:${REDIS_PORT}"
    volumes:
      - ${REDIS_VOLUME_SOURCE}:${REDIS_VOLUME_TARGET}
    networks:
      - ${NETWORK_NAME}
volumes:
  ${APP_VOLUME_SOURCE}:
  ${DB_VOLUME_SOURCE}:
  ${REDIS_VOLUME_SOURCE}:
networks:
  ${NETWORK_NAME}:
    driver: ${NETWORK_DRIVER}
Code language: JavaScript (javascript)

This template is designed to be modular, using environment variables for maximum configurability. Define these variables in a .env file.

Understanding Docker Network Modes Docker provides different network modes to connect containers and external resources.

Two commonly used modes are bridge and host.

Bridge Network

  • Default Network: Containers get isolated private IPs.
  • Use Case: Containers communicate with each other via their private IPs, and the host can access them through exposed ports.

Host Network

  • Shared Network: Containers share the host’s network stack.
  • Use Case: High-performance networking or when direct access to the host’s network is required.

Scenario: Mixing Network Modes You can mix network modes for services. For example:

  • App Service: Uses bridge mode for port mapping.
  • Database and Redis Services: Use host mode for direct access to resources on the host or other external hosts.

Example:

services:
  app:
    image: my-app-image
    container_name: my-app
    network_mode: "bridge"
    ports:
      - "8080:80"
    environment:
      - DATABASE_HOST=192.168.11.21
      - DATABASE_PORT=3306
      - REDIS_HOST=192.168.11.22
      - REDIS_PORT=6379
Code language: JavaScript (javascript)

Why Doesn’t localhost Work Inside a Container? Inside a Docker container, localhost refers to the container itself. If MySQL is running on the Docker host or another external machine, using localhost won’t work.

Instead, you can:

  1. Use Host IP Address: mysql -h 192.168.11.11 -P 3306 -u username -p
  2. Use Docker Gateway IP (172.17.0.1): mysql -h 172.17.0.1 -P 3306 -u username -p
  3. Use host.docker.internal (Modern Docker): mysql -h host.docker.internal -P 3306 -u username -p

Configuring MySQL for Remote Access To allow connections to MySQL from a container:

  1. Update MySQL Configuration: bind-address = 0.0.0.0
  2. Grant Access: GRANT ALL PRIVILEGES ON *.* TO 'username'@'%' IDENTIFIED BY 'password'; FLUSH PRIVILEGES;
  3. Check Firewall Rules: sudo ufw allow from <docker-host-ip> to any port 3306

Conclusion, Docker networking can seem complex at first, but understanding modes like bridge and host, and configuring services like MySQL correctly, can help you achieve seamless connectivity.

Use this guide to troubleshoot and optimize your Docker setups, ensuring containers can interact with hosts and external resources effortlessly.

Let us know if you have additional tips or challenges in the comments below!

Running Mattermost on Debian 12 Using Docker, without NGINX, and use External PostgreSQL

Mattermost is a powerful open-source collaboration tool that can be deployed in various ways, including using Docker for easy setup and management.

This tutorial will guide you through setting up Mattermost on Debian 12 using Docker, with an external PostgreSQL database configured in host network mode and without setting up Nginx.

Prerequisites

Before proceeding, ensure you have the following:

Debian 12 installed and updated.

Docker and Docker Compose installed. You can install Docker by following these commands

sudo apt update sudo apt install -y docker.io docker-compose
sudo systemctl start docker sudo systemctl enable docker

Sufficient resources for Mattermost, at least 4GB of RAM and a modern CPU.

Step 1, Create a .env File for Configuration

First, create a .env file to store all your credentials and configuration:

sudo vi .envCode language: CSS (css)

Add the following content to the .env file:

# APP mattermost
MM_ENABLE_OPEN_SERVER=true
MM_USERNAME=admin
MM_PASSWORD=dummypassword123
MM_DB_TYPE=postgres
MM_HTTP_PORT=8065

# DB postgres
POSTGRES_USER=dummyuser
POSTGRES_PASSWORD=dummypassword456
POSTGRES_DB=dummydb
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
Code language: PHP (php)

Ensure the POSTGRES_HOST is set to localhost because the host network mode allows the container to communicate directly with services running on the host.

Step 2: Create a Docker Compose File

Create a directory for Mattermost and navigate to it:

mkdir -p ~/mattermost-docker && cd ~/mattermost-docker
Code language: JavaScript (javascript)

Create a docker-compose.yml file:

nano docker-compose.yml
Code language: CSS (css)

Paste the following configuration into the file:

version: '3.8'

services:
  mattermost:
    image: mattermost/mattermost-team-edition:latest
    container_name: mattermost
    network_mode: "host"
    ports:
      - "${MM_HTTP_PORT}:${MM_HTTP_PORT}"
    environment:
      - MM_USERNAME=${MM_USERNAME}
      - MM_PASSWORD=${MM_PASSWORD}
      - MM_ENABLE_OPEN_SERVER=${MM_ENABLE_OPEN_SERVER}
      - MM_DB_TYPE=${MM_DB_TYPE}
      - MM_SQLSETTINGS_DRIVERNAME=postgres
      - MM_SQLSETTINGS_DATASOURCE=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}?sslmode=disable&connect_timeout=10
    volumes:
      - mattermost-data:/mattermost/data
      - mattermost-logs:/mattermost/logs
      - mattermost-config:/mattermost/config
    restart: unless-stopped

volumes:
  mattermost-data:
  mattermost-logs:
  mattermost-config:
Code language: JavaScript (javascript)

The network_mode: "host" setting ensures that the container uses the host’s network stack, allowing it to connect directly to the external PostgreSQL database.

Step 3, Start Mattermost with Docker Compose

Run the following command to start Mattermost:

sudo docker-compose up -d

Docker Compose will download the Mattermost image and start the service. This may take a few minutes if you’re running it for the first time.

Step 4, Verify Mattermost is Running

Check the running containers:

sudo docker ps

You should see a container named mattermost running. To verify further, open your web browser and navigate to:

http://<your-server-ip>:8065
Code language: JavaScript (javascript)

You should see the Mattermost setup page.

Step 5: Configure Initial Setup

In many case, when the application is open from web, the first user is the admin user. Make sure you access in incognito mode, because sometime cache is making the first resgistration page is not working.

In other case maybe you can input the admin user and password form .env file

Username: admin

Password: dummypassword123

Follow the on-screen instructions to complete the setup.

Step 6: Managing the Mattermost Service

To stop Mattermost, run:

sudo docker-compose down

To restart Mattermost, run:

sudo docker-compose up -d

Comparing Mattermost Team Edition and Enterprise Edition

When choosing between the Team Edition and Enterprise Edition of Mattermost, it’s important to understand their differences:

FeatureTeam EditionEnterprise Edition
CostFreeRequires a paid license
User LimitsUnlimited usersUnlimited users
Core Collaboration FeaturesIncludedIncluded
Compliance & AuditingNot availableAvailable
Advanced User ManagementNot availableAvailable
High AvailabilityNot supportedSupported
Priority SupportCommunity support onlyEnterprise-grade support
Performance MonitoringBasicAdvanced
Custom IntegrationsLimitedAdvanced integrations

What are Custom Integrations?

Custom integrations refer to the ability to extend and connect Mattermost with other tools and services to meet specific organizational needs. Here’s how they differ between the Team and Enterprise editions:

  • Team Edition:
    • Limited integrations are available.
    • Supports basic webhook functionality to send and receive messages between Mattermost and external systems.
    • Can integrate with common tools such as Slack-compatible bots and integrations.
  • Enterprise Edition:
    • Offers advanced integration capabilities, including API access for deeper customization.
    • Supports Single Sign-On (SSO) with SAML, LDAP, or Active Directory.
    • Includes advanced webhook functionalities and allows for building highly customized workflows.
    • Enhanced scalability to manage integrations across large teams or multiple departments.

If your organization relies on complex workflows, custom bots, or needs tight integration with enterprise systems, the Enterprise Edition may be necessary. Otherwise, the Team Edition’s basic capabilities are sufficient for most small-to-medium teams.

Troubleshooting

  • Logs: If you encounter any issues, you can check the logs using: sudo docker logs mattermost
  • Updating Mattermost: To update Mattermost to the latest version, stop the container, pull the latest image, and restart the service: sudo docker-compose down sudo docker pull mattermost/mattermost-team-edition:latest sudo docker-compose up -d

Final words

You have successfully set up Mattermost on Debian 12 using Docker with an external PostgreSQL database and host network mode.

This setup is suitable for production environments and provides better scalability and reliability compared to SQLite. For additional scalability, consider adding external database backup solutions and reverse proxy configurations.

Service Xiaomi Mi 10T Pro, Membangkitkan yang Mati

Minggu lalu, handphone alias gawai andalan yang sudah menemani berkaktivitas selama beberapa tahun, mendadak mati ketika sedang melakukan perjalanan komuter di kereta.

Beberapa gejala gangguan memang sudah saya rasakan dan alami sebelumnya, handphone yang sering tiba-tiba lambat alias lemot lalu ‘sembuh’ sendiri, layar yang kedap-kedip dan bergerak scroll sendiri, serta indikator pengisian baterai yang sering menginformasikan angka yang salah.

Pada akhirnya, segala ganguan tersebut, termasuk bila handphone anda tiba-tiba suaranya hilang dan timbul kembali, biasanya itu karena Central Processing Unit (CPU) yang akan segera mati alias rusak.

Mengapa hal tersebut diatas bisa menjadi indikasi ? Detail ceritanya ada di artikel blog ini ya …

Model handphone yang tiba-tibat freeze layarnya, lalu mati dan tidak bisa dicharge tersebut adalah Xiaomi Mi 10T Pro 5G yang saya beli pada Bulan Maret 2021.

Artinya, umur perangkat atau processornya sekitar empat (4) tahun, sampai pada akhirnya komponen tersebut rusak.

Setelah memindahkan akses penting Gmail dan aplikasi lainnya ke gawai cadangan dan memastikan semua akses aplikasi dari perangkat tersebut dihapus atau dinonaktifkan, akhirnya saya bulatkan tekat untuk membawa handhone Xiaomi Mi 10T Pro tersebut ke layanan service resmi Xiaomi di Kota Bogor.

Bila handphone akan berpindah tangan, termasuk ke layanan service resmi atau lainnya, demi keamanan, sebaiknya handphone tersebut dikosongkan dulu dengan metode factory reset..

Kenapa ini harus dilakukan ? sepertinya saya akan buatkan di artikel blog terpisah.

Karena handphone mati dan tidak bisa boot untuk factory reset, akhirnya saya harus memutuskan akses berbagai aplikasi penting di perangkat tersebut dari perangkat cadangan, mengganti passwordnya dari perangkat lain dan juga komputer.

Layanan Pelanggan Resmi Xiaomi

Tidak terlalu berharap banyak dari layanan resmi pelanggan Xiaomi Indonesia, tapi layak dicoba.

Sebelum saya harus menabung untuk membeli handphone dengan kemampuan yang setara, saya mencoba peruntungan dengan membawa perangkat tersebut ke layanan resmi pelanggan Xiaomi di Bogor.

Ilustrasi pusat layanan pelanggan Xiaomi di Indonesia

Setelah masuk ruang tunggu dan mengambil nomor antrian di kantor layanan pelanggan Xiaomi, Bogor, saya menunggu santai bersama ‘pasien’ lainnya.

Tidak lama kemudian saya dilayani oleh Mbak customer service dan sesuai prosedur, handphone harus ditinggal, menunggu selama 2 jam untuk diperiksa oleh teknisi.

“Estimasi biaya akan kita kabari dua jam lagi ya Pak, dan juga ada resiko data tidak bisa diselamatkan dan juga dikenakan biaya pemeriksaan sekitar Rp 50.000,- rupiah”, ujar Mbak customer service.

Setelah tandatangan surat pernyataan service, saya akhirnya makan siang dan jalan-jalan dulu di Botani Square, Bogor. Nanggung, dua jam kalo pulang dulu pasti kena macet.

Kurang dari dua jam, datanglah, kabar yang ditunggu itu melalui pesan singkat WhatsApp.

“Kerusakan pada mainboard, estimasi biaya untuk pergantian perangkat tersebut adalah Rp 5.931.000,-“, begitu pesan pada layar aplikasi WhatsApp.

Tidak terkejut juga, karena saya sudah ‘legowo’, tetapi paling tidak saya mengetahui harga maksimal bila ingin menghidupkan kembali smartphone tersebut.

Bagi layanan resmi pelanggan resmi Xiaomi Indonesia, yang mereka lakukan pastinya sesuai SOP, mengganti seluruh modul motherboard, dan pastinya bukan memperbaiki berbagai komponen dan chipset yang menempel pada modul tersebut.

Sama saja dengan perbaikan mobil ke bengkel resmi, mekaniknya bukan memperbaiki, tetapi mengganti part mobil yang rusak.

Sebenarnya tidak ada informasi detail mengenai modul atau komponen apa yang rusak, mereka hanya bilang ‘motherboardnya’ rusak, mungkin maksudnya seluruh board besarta komponen diatasnya, antara lain CPU, Memory, Storage dan komponen lainnya.

Tanpa menunggu dan berpikir panjang saya balas pesan singkat tersebut, dan bergegas kembali ke kantor layanan pelanggan Xiaomi untuk mengambil handphone tersebut.

Setelah mengantri dan mendapatkan kembali perangkat handphone dengan membayar biaya pemeriksaan, saya bergegas kembali menuju ke parkiran mobil.

Layanan Service Non Resmi, Java Phone Service

Ketika ingin masuk mobil, pandangan saya tertuju ke arah Mall Jambu Dua, Bogor, bangunannya terlihat dari parkiran layanan pelanggan Xiaomi, Bogor.

Seingat saya, mall jadul tersebut sedang berbenah dan memiliki dua lantai dimana banyak jasa layanan service perangkat elektronik mulai dari laptop sampai dengan handphone.

Iseng saya membuka aplikasi Google Maps di perangkat cadangan yang saya gunakan, dan mengetikkan keyword “service handphone terdekat” pada kolom pencarian.

Seketika, muncul beberapa titik di layar dan ada beberapa titik yang berada di dalam area Mall Jambu Dua, Bogor.

Saya zoom in, dan klik salah satu titik di dalam Mall Jambu Dua, Bogor yang memiliki rating tinggi dan mulai membaca berbagai testimonial alias komentarnya dengan mengurutkan dahulu daftar testimoni yang paling baru.

JAVA PHONE SERVICE, itu adalah nama yang tertera pada Google Maps.

Karena penuh dengan ulasan baik, akhirnya saya mengarahkan mobil ke parkiran Mall Jambu Dua, Bogor.

“Nggak ada salahnya dicoba, lokasinya dekat, nothing to loose …”, begitulah pikirku saat itu.

Lokasi tokonya cukup mudah ditemukan karena sesuai yang tertera pada Google Maps, JAVA PHONE SERVICE, Lantai Semi Dasar, A3 Nomor 11, Mall Jambu Dua, bogor.

Bila masuk dari Lobi depan, langsung masuk ke lantai bawah, Lantai Semi Dasar, jalan lurus ketemu gang kedua, belok kiri, lihat saja papan nama toko, makin ke kiri, angkanya makin kecil.

A5, A4, lalu A3, cari nomor 11.

Tanpa babibu dan banyak basa basi, saya langsung menyampaikan kondisi handphone saya ke Mas penjaga, akhirnya saya tahu namanya Mas Rahmat, dia yang membantu Mas Is pemilik dan teknisi pemilik toko service tersebut.

“Wah Mas, ini Mi 10T lagi ada beberapa yang masuk, rusaknya sama, CPU”, ujarnya dengan yakin.

“Heh, serius, kok bisa yakin CPU, bisa dibenerin nggak?” tanyaku setengah terkejut.

“Iya, ini baru ada handphone yang masuk, tipenya sama, kena CPU, gejala awalnya suaranya sering hilang”, ujarnya menjelaskan.

“Kita cek dulu, kalo diganti CPUnya dan nyala, biayanya Rp 750.000,- ya Oom” ujarnya memberikan informasi.

“Kalo nggak bisa nyala kena berapa”, tanyaku.

“Nggak kena biaaya Oom”, katanya dengan santai.

Wow… biayanya sangat ‘bersahabat’ ini, tanpa pikir panjang, saya titipkan handphone tersebut ke Mas Rahmat, katanya waktu pengerjaannya 1 – 3 hari, nanti dikabarkan oleh Mas Is via WhatsApp.

Malam hari sekitar jam 20:00 WIB, saya mendapat kabar bahwa handphone sudah selesai diperbaiki dan bisa diambil.

“Wow, cepat sekali pengerjaannya”, pikirku.

Setelah bertanya jam buka toko, yaitu antara jam 11 pagi sampai jam 8 malam, akhirnya saya menjadwalkan kedatangan esok harinya saja sebelum jam makan siang.

Selesai, Pengambilan Unit

Sesampainya di toko, akhirnya saya bisa bertemu dengan Mas Is, lalu ngobrol-ngobrol sedikit, sambil menunggu pengetesan akhir perangkat handphone tersebut.

Pengetesan standar, fitur tombol, suara, koneksi dan lainnya, terakhir setelah semua berfungsi dengan baik, handphone kemudian ditutup dan direkatkan kembali dengan lem.

Selama proses menunggu tersebut, Mas Is sedang mengerjakan handphome Xiaomi Mi 10T Pro 5G yang lain, yang sama modelnya dengan handphone saya.

Jadi total ada tiga (3) perangkat Xiaomi Mi 10T Pro 5G yang sedang diperbaiki CPU nya.

“Ini memang sudah waktunya, itu cpu yang kodenya snap snap itu lah … pada rusak semua, ya kalo handphone paling umur dua atau tiga tahun saja”, ujarnya sambil bekerja menggunakan mikroskop.

“Oh gitu ya, jadi lumayan dong handphone saya sampai empat tahun”, membalas pemaparan Mas Is.

“Lalu itu part CPU dari mana penggantinya, kanibal atau baru?”, tanyaku dengan penuh penasaran.

“Kalo komponen, juga temasuk CPU itu sudah ada jalur suplynya dari China sana, itu CPU baru lewat jalur import”, katanya menjelaskan.

Dikarenakan perangkat handphone sudah bisa menyala dan berfungsi dengan baik, tanpa memperpanjang waktu lagi, akhirnya saya melakukan pelunasan pembayaran dan meminja ijin untuk mengambil gambar.

“Mas Is, Mas Rahmat ijin ya ambil gambar, untuk testimoni di Google Maps”, ujarku

“Ok silahkan Mas”

Java Phone Service, Mall Jambu Dua, Bogor

Setelah memindahkan SIM card kembali dari handphone cadangan ke handphone yang selesai diperbaiki tersebut, akhirnya saya pamit pulang.

Dari pengalaman ini, untuk kawasan Bogor dan sekitarnya, layanan JAVA PHONE SERVICE sangat saya rekomendasikan, kalo ada masalah dengan handphone atau laptop pastinya akan saya ke tempat ini lagi.

Where Docker Stores Images on Debian 12 and How to Customize the Storage Directory

Continuing from our previous article on setting up Docker and running Docker Compose on Debian 12 Bookworm, let’s dive deeper into understanding where Docker stores its image files and how to customize the storage directory for better management.

Default Docker Image Storage Location on Debian 12

By default, Docker stores all its data, including images, containers, volumes, and other artifacts, under the following path:

/var/lib/docker
Code language: JavaScript (javascript)

This directory contains subdirectories managed by the storage driver Docker uses. For instance, if the overlay2 storage driver is used (the default for most Linux distributions, including Debian 12), images and their layers will be stored in:

/var/lib/docker/overlay2
Code language: JavaScript (javascript)

You can verify the current Docker root directory by running:

docker info | grep "Docker Root Dir"
Code language: JavaScript (javascript)

This command outputs the current path where Docker stores its data:

Docker Root Dir: /var/lib/docker
Code language: JavaScript (javascript)

While the default location works for most setups, there are scenarios where customizing the storage path is beneficial, such as:

  • Low disk space on the default partition.
  • Organizing Docker data on a dedicated storage drive or partition.
  • Simplifying backups and maintenance.

Customizing Docker’s Storage Directory

To customize where Docker stores its images and other data, follow these steps:

Step 1, Create a New Directory for Docker Data

Choose a new location for Docker’s data, such as /mnt/docker-data. Create the directory and ensure appropriate permissions:

sudo mkdir -p /mnt/docker-data
sudo chown -R root:root /mnt/docker-data
sudo chmod -R 700 /mnt/docker-data
Step 2, Update Docker Configuration

Docker’s storage directory can be updated by modifying its daemon configuration file:

  1. Open the Docker daemon configuration file: sudo nano /etc/docker/daemon.json
  2. Add or update the data-root field to point to the new directory: { "data-root": "/mnt/docker-data" }

If the file doesn’t exist, create it and add the above content.

Step 3, Stop Docker and Move Existing Data

Before restarting Docker, stop the service and move existing data to the new directory:

sudo systemctl stop docker
sudo rsync -a /var/lib/docker/ /mnt/docker-data/
Code language: JavaScript (javascript)

This command ensures that all existing data is safely copied to the new location. Once completed, rename or backup the old directory:

sudo mv /var/lib/docker /var/lib/docker.bak
Code language: JavaScript (javascript)
Step 4, Restart Docker

Restart the Docker service to apply the changes:

sudo systemctl start docker

Verify that Docker is now using the new storage directory:

docker info | grep "Docker Root Dir"
Code language: JavaScript (javascript)

The output should now display the updated path, e.g.:

Docker Root Dir: /mnt/docker-data
Code language: JavaScript (javascript)
Step 5, Test the Setup

Pull an image and verify it is stored in the new directory:

docker pull hello-world
ls /mnt/docker-data

You should see the newly pulled image data in the customized path.

Reverting to Default Settings

If you encounter issues and need to revert to the default storage location, simply:

  1. Stop Docker: sudo systemctl stop docker
  2. Restore the original directory: sudo mv /var/lib/docker.bak /var/lib/docker
  3. Remove the data-root entry from /etc/docker/daemon.json or set it back to /var/lib/docker.
  4. Restart Docker: sudo systemctl start docker

Final Words …

Customizing Docker’s storage directory on Debian 12 can help optimize disk usage and improve data management.

By following the steps above, you can easily configure Docker to use a different location for storing images and containers.

As always, test changes thoroughly in your environment and ensure backups are in place before making significant modifications.

Cara Paling Mudah Akses Situs yang Diblokir

Beberapa kali saya dihadapkan akan kebutuhan untuk mengakses situs-situs yang diblokir pihak Kominfo (sekarang Komidigi).

Kebutuhan ini pastinya untuk urusan yang postif ya, terkait pekerjaan dan profesi saya saat dalam membuat aplikasi, riset dan juga mengelola berbagai sistem, server di berbagai datacenter di seluruh dunia.

Diluar kontroversi mengapa situs-situs yang sebenarnya bermanfaat atau banyak manfaatnya tersebut juga ikut diblokir, saya tetap berharap pihak penyelenggara dan regulator akses Internet di Indonesia lebih bijak dalam mengkategorikan sebuah situs perlu diblokir atau tidak.

Masalahnya, pekerjaan atau kebutuhan profesi saya mengakses situs-situs yang diblokir tersebut tidak bisa ditunda apalagi diabaikan.

Banyak cara untuk mengatasi blokir akses Internet Indonesia, mulai dari cara paling ribet sampai paling mudah.

Untuk komputer laptop atau desktop dirumah, ketika saya ingin mengakses situs Reddit dan Vimeo yang diblokir, saat ini saya mengandalkan aplikasi WARP Cloudflare.

WARP Cloudflare adalah aplikasi VPN (Virtual Private Network) gratis yang berfungsi melindungi koneksi internet pengguna dengan mengenkripsi data yang dikirimkan dan diterima, mulai sejak awal dari komputer atau perangkat yang digunakan.

Cara instalasi dan penggunaannua cukup mudah, unduh aplikasi WARP Cloudflare dari situs resminya dan lakukan instalasi pada perangkat komputer. Bila ingin digunakan, aktifkan dengak klik tombol aktivasi, dan bila ingin dinonaktifkan, klik kembali tombolnya.

Pilihan tombol tersebut tersedia dengan mengakses tray icon di sudut kanan bawah sistem Operasi Windows 10/11.

Bila warna awannya berwarna abu-abu, artinya non aktif, anda menggunakan koneksi Internet biasa, bila icon awannya berwarna oranye, artinya anda menggunakan koneksi WARP Cloudflare yang dapat mengakses situs yang diblokir.

Bila anda sering menggunakan fasilitas WIFI publik untuk mengakses Internet seperti di lokasi kafe, hotel dan bandara, saya sarankan Anda menggunakan dan mengaktifkan WARP Cloudflare untuk meningkatkan keamanan dan menjamin privasi koneksi anda.

Banyak penjahat yang memanfaatkan akses internet publik untuk menyadap dan mendapatkan informasi sensitif untuk disalahgunakan.

Dengan WARP Cloudflare, tidak hanya anda bisa mengakses situs yang diblokir, anda juga mendapatkan bantuan kemamanan akses Internet dimanapun Anda berada.