Skip to content

Feature: Add external database volume and external database connection#155

Open
nshandra wants to merge 4 commits intodevelopmentfrom
feature/external-database
Open

Feature: Add external database volume and external database connection#155
nshandra wants to merge 4 commits intodevelopmentfrom
feature/external-database

Conversation

@nshandra
Copy link
Copy Markdown
Contributor

@nshandra nshandra commented Dec 5, 2025

📌 References

📝 Implementation

Add parameters to d2-docker start to allow either:

  • Store the DB in an external persistent volume
  • Use an external DB instead of a container

Testing

External Persistent Volume Case

This case will store the DB volume at the specified absolute path.

./d2-docker-dev.sh --log-level=DEBUG start <data_image> --external-db-volume="/absolute/path/to/volume_dir"

External DB Case

This case needs a psql DB runnig in the machine hosting the dockers or in a accessible network.

This DB has to have the correct settings:

  • /etc/postgresql/{ver}/main/postgresql.conf

    Set listen_addresses to:

    • '*' (easiest for testing)
    • 'localhost,172.17.0.1' (docker and DB in same system)
    • 'localhost,<reachable_ip>'
  • /etc/postgresql/{ver}/main/pg_hba.conf

    Add an entry to allow the connection. Example:

    # Docker bridge subnet (check with: docker network inspect bridge)
    host    dhis2           dhis            172.17.0.0/16           scram-sha-256
    # LAN
    host    dhis2           dhis            192.168.0.0/24          scram-sha-256

Running d2-cloner:

./d2-docker-dev.sh --log-level=DEBUG start <data_image> --external-db-url="postgresql://user:pass@host:port/db"

#1ck80y8

@nshandra nshandra requested a review from tokland December 5, 2025 08:55
@nshandra nshandra self-assigned this Dec 5, 2025
@nshandra nshandra marked this pull request as draft December 5, 2025 13:06
@nshandra nshandra marked this pull request as ready for review January 21, 2026 21:28
@tokland
Copy link
Copy Markdown
Contributor

tokland commented Jan 26, 2026

@nshandra Sorry, I had this pending. Can you merge with development? (re-add me when done)

@nshandra
Copy link
Copy Markdown
Contributor Author

@tokland done.

@tokland tokland requested review from tokland and removed request for tokland January 26, 2026 09:13
Copy link
Copy Markdown
Contributor

@tokland tokland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Features worked for me (only I had to add a trailing comma, probably a merge artifact)

In-line comments about the code:

@@ -300,6 +318,9 @@ def run_docker_compose(
("ROOT_PATH", ROOT_PATH),
("PSQL_ENABLE_QUERY_LOGS", "") if not enable_postgres_queries_logging else None,
("GLOWROOT_PORT", get_port_glowroot(glowroot_port))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing trailing comma, the code does not run as-is.

"--external-db-volume",
metavar="DIRECTORY",
help="Directory for external database volume",
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This option worked for me. Only problem is that the final permissions of the folder are off:

$ ls -al | grep volume
drwx------  19    70    70 4.0K Apr 14 10:21 volume

However, that's something d2-docker has been ignoring all along - we probably should have user: "${UID}:${GID}" or similar in our docker-compose to avoid this kind of problems. Not sure it's the moment to try that (it may have unintended consequences), but I'd be a step in the good direction.

type=str,
metavar="postgresql://user:pass@host:port/dbname",
help="Use external PostgreSQL database"
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It worked for me

"--load-dump-from-data",
action="store_true",
help="Load database dump from data container (only with --external-db-url)",
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It worked for me, only that I had to explicitly create the database with psql for it to work. Is that the intended workflow?

else
echo "Normal mode: $path"
$psql_cmd < "$path"
$psql_cmd <"$path"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting is being applied to code outside of the PR. It would be fine if the formatting were incorrect, but we don't have a defined prettifier, so there is no "incorrect" for now. Would the best approach be a .vscode/settings.json file where we set up the formatter and its options?

("GLOWROOT_PORT", get_port_glowroot(glowroot_port))
("EXTERNAL_DB_VOLUME", external_db_volume) if external_db_volume else None,
("EXTERNAL_DB_URL", external_db_url) if external_db_url else None,
("LOAD_DUMP_FROM_DATA", "yes" if load_dump_from_data else "no") if external_db_url else "no",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The grouping/parentheses look off, right?

logger.info("Validating external database connection...")
try:
psql_cmd = ["psql", "-d", db_url, "-c", "SELECT now();", "&> /dev/null"]
run(psql_cmd, capture_output=False)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems it worked, but I am curious about "&> /dev/null", since the default for run is shell=False.


if args.external_db_url:
utils.validate_external_db_connection(args.external_db_url)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a check to raise error if both options are passed, as they are mutually exclusive?

Also, a standalone --load-dump-from-data is not correct workflow.

psql_cmd="$psql_base_cmd -v ON_ERROR_STOP=0"
psql_strict_cmd="$psql_base_cmd -v ON_ERROR_STOP=1"
pgrestore_cmd="pg_restore -h db -U dhis -d dhis2"
pgrestore_cmd="pg_restore ${db_url:-"-h db -U dhis dhis2"}"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before we had -d dhis2 but now it's directly dhis2. Is that correct?

if parsed.hostname == "localhost":
hostname = "host.docker.internal"
else:
hostname = parsed.hostname
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] we can use hostname = ..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants