Per-project Postgres with asdf and direnv
source link: https://ylan.segal-family.com/blog/2021/07/23/per-project-postgres-with-asdf-and-direnv/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Per-project Postgres with asdf and direnv
Jul 23, 2021 • Ylan Segal • asdf, direnv, postgres, unix
My development computer typically has a number of different projects, each needing specific versions of some tools. Previously, I wrote about using asdf
to manage my ruby, elixir, crystal and erlang version. I’ve been using it successfully to manage Postgres versions as well.
What I get
- Precisely manage the Postgres version (e.g.
10.14
,12.5
) for each project. - The Postgres data directory lives alongside other project files
- No collision between projects. Each can have their own Postgres server running simultaneously.
I use asdf and asdf-postgres to install and manage Postgres. After installing and setting up, I select the version of Postgres by creating a .tool-versions
file in the root of the project:
# .tool-versions
postgres 10.14
This directs the Postgres installation to use 10.14
:
$ psql --version
psql (PostgreSQL) 10.14
Postgres servers by default are configure to open port 5432
. Running multiple Postgres servers would cause collision, and require picking different ports. Instead of assigning a new port number to each server, I direct Postgres to use Unix sockets instead.
In order to automate the needed setup, I use direnv. I use a custom layout function for Postgres that takes care of setting up Postgres correctly, in ~/.direnvrc
:
#!/usr/bin/env bash
# direnv customization
# Initializes per-project postgres
layout_postgres() {
PGDATA="$(direnv_layout_dir)/postgres"
PGHOST="$PGDATA"
export PGDATA PGHOST
if [[ ! -d "$PGDATA" ]]; then
log_status "Initializing postgres for this project..."
initdb
cat >> "$PGDATA/postgresql.conf" <<-EOF
listen_addresses = ''
unix_socket_directories = '$PGHOST'
EOF
echo "CREATE DATABASE $USER;" | postgres --single -E postgres
fi
}
On first use for a project, it will create a the needed directories, and configure Postgres to not listen on any IP address. It also configures where to look for Unix sockets.
Note that the direnv_layout_dir
is typically .direnv
under the project. That means that the Postgres data will leave in .direnv/postgres
.
To invoke on each project, I add a .envrc
file:
# .envrc
layout postgres
The first time direnv
sees the file, it will prompt to use direnv allow
. On subsequent invocations, it will only setup the correct environment variables.
The last part, is to start the server. It could be automated, but I typically start myself as needed on each project with:
$ pg_ctl status || pg_ctl start
You can start as many Postgres servers as you need. I don’t see much performance impact on my machine, even with ~5 servers running.
Rails configuration will respect that PGDATA
and PGHOST
, and will work seamlessly. Here is what my current config/database.yml
looks like:
login: &login
adapter: postgresql
# For local servers
development:
<<: *login
database: my_project_development
test:
<<: *login
database: my_project_test
As expected, psql
also respects the environment variables:
$ psql my_project_development -c 'SELECT NOW()'
now
-------------------------------
2021-07-23 12:01:16.886723-07
(1 row)
Time: 0.365 ms
Conclusion
This setup allows me to manage the version of Postgres on a per-project basis, with minimal setup. It requires a bit of direnv
customization, and then to opt-in on each project by configuring asdf
and direnv
:
# .tool-versions
postgres 10.14
# .envrc
layout postgres
asdf
and direnv
play well together, even though neither has specific knowledge of the other. The Unix dream.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK