# pg\_tls rejects libpq 17+ clients ("no application protocol"), and the postgresql ALPN can't be configured

**URL:** <https://community.fly.io/t/pg-tls-rejects-libpq-17-clients-no-application-protocol-and-the-postgresql-alpn-cant-be-configured/28898>\
**Category:** General\
**Tags:** postgres\
**Created:** [October 22, 2026, 7:25pm UTC](https://community.fly.io/t/pg-tls-rejects-libpq-17-clients-no-application-protocol-and-the-postgresql-alpn-cant-be-configured/28898 "2026-10-22T19:25:16Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![nathanjcochran](https://avatars.discourse-cdn.com/v4/letter/n/54ee81/32.png) [@nathanjcochran](https://community.fly.io/u/nathanjcochran)\
**Post date:** [October 22, 2026, 7:25pm UTC](https://community.fly.io/t/pg-tls-rejects-libpq-17-clients-no-application-protocol-and-the-postgresql-alpn-cant-be-configured/28898/1 "2026-10-22T19:25:16Z")

</div>

We run a public Postgres-protocol service on Fly using the `pg_tls` handler, with a Fly-managed Let’s Encrypt cert for our custom domain. Clients using libpq 17 or newer (psql 17+, psycopg, pgAdmin 8.13+, etc.) can’t connect over TLS.

**Setup**

```toml
[[services]]
  protocol = "tcp"
  internal_port = 5432

  [[services.ports]]
    port = 5432
    handlers = ["pg_tls"]

```

**What happens**

Since version 17, libpq always sends the ALPN protocol `postgresql` in the TLS ClientHello, and there’s no way to turn that off. The `pg_tls` handler seems to advertise only `http/1.1`. With no overlap, the proxy aborts the handshake:

```auto
$ psql "host=<our-domain> sslmode=require ..."
psql: error: connection to server at "<our-domain>" (...), port 5432 failed:
SSL error: tlsv1 alert no application protocol

```

With `sslmode=prefer` (the libpq default), the client silently falls back to an unencrypted connection, so credentials are sent in plaintext.

**Reproduction with openssl**

```auto
# Fails with alert 120 (no_application_protocol):
openssl s_client -connect <app>.fly.dev:5432 -starttls postgres -servername <app>.fly.dev -alpn postgresql

# Also fails:
openssl s_client -connect <app>.fly.dev:5432 -starttls postgres -servername <app>.fly.dev -alpn h2

# Succeeds, negotiating "ALPN protocol: http/1.1":
openssl s_client -connect <app>.fly.dev:5432 -starttls postgres -servername <app>.fly.dev -alpn http/1.1

```

**What we tried**

A workaround in an earlier community thread ([Migrate from Unmanaged Postgres to Neon](https://community.fly.io/t/migrate-from-unmanaged-postgres-to-neon/26381)) is to set:

```toml
    tls_options = { alpn = ["postgresql"] }

```

Both `fly deploy` and a direct Machines API update now reject this:

```auto
Services [0] Ports [0] Tls options Alpn must be one of: h2, http/1.1

```

Setting `alpn: []` through the Machines API is accepted but stored as `tls_options: {}`, and the behavior doesn’t change. We’re on flyctl v0.4.120.

**Request**

Could the `pg_tls` handler either:

1. advertise/accept the `postgresql` ALPN by default (the same check libpq 17+ makes against Postgres itself), or
2. allow `postgresql` as a value in `tls_options.alpn`?

As things stand, `pg_tls` can’t serve TLS to any current libpq-based client, which is most Postgres tooling. Fly-managed certs can’t be exported, so terminating TLS in our own app would mean renewing certs ourselves.

---

<div class="post-metadata">

**Author:** ![PeterCxy](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/petercxy/32/36155_2.png) [@PeterCxy](https://community.fly.io/u/PeterCxy)\
**Post date:** [October 22, 2026, 7:31pm UTC](https://community.fly.io/t/pg-tls-rejects-libpq-17-clients-no-application-protocol-and-the-postgresql-alpn-cant-be-configured/28898/2 "2026-10-22T19:31:45Z")

</div>

Thanks for flagging this, we’ll look into adding this, though we probably couldn’t guarantee an ETA for this at this point.
