How to use PlanetScale with Phoenix on Fly.io
6th June, 2026
I had a small Elixir Phoenix app that I wanted to deploy somewhere for testing. I reached for Fly.io because I’ve played around with the service before and found it interesting. Unfortunately Fly don’t offer the cheap good-enough-for-testing self-managed Postgres they used to. They only offer Managed Postgres at Managed Postgres prices now.
I decided to give PlanetScale a go.
The next step should be as simple as as setting the DATABASE_URL correctly,
right? Unfortunately not.
These are the configuration changes I made to get connected to PlanetScale from my Phoenix app. You may have to make additional changes to get your app fully deployed.
Deploy the app
First off, I started the process of deploying my app to Fly.
We’re going to set the DATABASE_URL.
fly secrets set DATABASE_URL="postgresql://<username>:<password>@aws-eu-west-2-1.pg.psdb.cloud:5432/postgres?sslmode=verify-full&sslrootcert=system"
Now, in the app directory:
fly launch
This will generate a Dockerfile and other config needed for deploying to Fly.
It won’t be quite right though, so we’ll need to tweak it. The deploy is going
to fail.
Configure SSL
Postgres on PlanetScale is SSL only, so we need to enable that in
config/runtime.exs.
maybe_ipv6 = if System.get_env("ECTO_IPV6") in ~w(true 1), do: [:inet6], else: []
config :example_app, ExampleApp.Repo,
- # ssl: true,
+ ssl: true,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
# For machines with several cores, consider starting multiple pools of `pool_size`
# pool_count: 4,
socket_options: maybe_ipv6
IPv6 configuration
When Fly configures your app it sets it up to work with their own internal networking, which uses IPv6 – very modern. This doesn’t work when we are trying to connect to PlanetScale though.
We need to tell Ecto to not use IPv6 in rel/env.sh.eex. This file will have
been generated by fly launch.
#!/bin/sh
if [ -n "$FLY_APP_NAME" ]; then
export DNS_CLUSTER_QUERY="${FLY_APP_NAME}.internal"
export RELEASE_NODE="${FLY_APP_NAME}-${FLY_IMAGE_REF##*-}@${FLY_PRIVATE_IP}"
# configure node for distributed erlang with IPV6 support
export ERL_AFLAGS="-proto_dist inet6_tcp"
+ export ECTO_IPV6="false"
fi
Database pool size
Now, that should technically be it for getting us connected to the database, but
I got errors due to the amount of database connections. I just wanted this app
to work, so I lowered the number in config/runtime.exs and it worked.
You should likely not follow this advice.
maybe_ipv6 = if System.get_env("ECTO_IPV6") in ~w(true 1), do: [:inet6], else: []
config :example_app, ExampleApp.Repo,
ssl: true,
url: database_url,
- pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
+ pool_size: String.to_integer(System.get_env("POOL_SIZE") || "2"),
# For machines with several cores, consider starting multiple pools of `pool_size`
# pool_count: 4,
socket_options: maybe_ipv6
Additionally, it should be noted that I am connecting directly to Postgres in
this example, not via PlanetScale’s PgBouncer setup, which uses port other than
5432.
This should get you connected.