Airport
ATTACH an Arrow Flight server as a DuckDB catalog and query it in SQL.
On this page
Deep Dive
Technical Details
What Airport gets you
If you operate a service that already speaks Arrow Flight — a custom Python or Go data API, an internal federation gateway, or a Flight-native system like Dremio or InfluxDB 3 — Airport makes it queryable from DuckDB with a single ATTACH:
CREATE OR REPLACE SECRET my_service ( TYPE airport, scope 'grpc+tls://flight.example.com:8815', auth_token :token);
ATTACH 'grpc+tls://flight.example.com:8815' AS svc (TYPE airport);
SELECT user_id, COUNT(*) AS eventsFROM svc.public.activityWHERE ts >= CURRENT_DATE - 7GROUP BY user_id;No client library, no DataFrame round-trip, no separate query API on top of your service. Just SQL.
Airport translates DuckDB SQL into Arrow Flight RPC calls. Anything the protocol can express, Airport can carry; anything beyond what your Flight server implements doesn’t suddenly start working because Airport is loaded.
- Filter pushdown runs to the extent your server honors
GetFlightInfofilter predicates. Servers that don’t push filters down will see DuckDB scan the whole result and filter locally. - Mutation (
INSERT/UPDATE/DELETE) requires the server to implementDoPutfor those operations. Read-only servers stay read-only. - Schema discovery depends on the server exposing a
ListFlights/ catalog interface. Servers that only answer specific descriptors won’tATTACHcleanly — useairport_take_flightfor one-off reads instead.
What can I connect to?
The honest answer: any service that implements Arrow Flight. In practice that’s a narrower list than “data systems that use Arrow internally.”
Confirmed Flight-speaking systems:
- Custom services built with
pyarrow.flight.FlightServerBase, the Arrow Java Flight library, or the Arrow Go Flight package. This is where Airport’s value is unambiguous — you write a 30–50 line Flight server, point Airport at it, and your service is queryable from SQL with zero client SDK on the consumer side. - Dremio — exposes Flight SQL natively.
- InfluxDB 3 — documented Flight endpoint for query.
Not Arrow Flight, even if they use Arrow internally:
Snowflake, Microsoft Fabric, BigQuery, and several other warehouses use Arrow as their result-format wire encoding but don’t expose a public Flight server you can ATTACH to. Airport won’t help you here. For database-style remote query against systems with ADBC drivers, see the sibling adbc_scanner extension.
Who Airport is for
The most underrated angle: platform and data engineering teams who already run an internal data service. If your service produces tabular data and you’re considering inventing a SQL query API on top of it (translating predicates, paginating, handling joins), don’t. Implement pyarrow.flight.FlightServerBase — a few dozen lines — and your service inherits SQL access for free via Airport. DuckDB becomes the query engine; you just write the data-fetch path.
The other natural fit is federation: DuckDB joining across multiple Flight backends (your custom service, a Dremio instance, an InfluxDB measurement) in one query, without an orchestration layer.
How it integrates with DuckDB
ATTACH ... (TYPE airport)— full catalog integration. Schema discovery, joins across local and remote tables, query pushdown where the server supports it, andINSERT/UPDATE/DELETEto writable backends.- Table functions —
airport_flights,airport_take_flight,airport_action,airport_databasesfor ad-hoc / streaming use withoutATTACH. - Secret type
airport— managed credentials (location,auth_token,criteria) passed by name to all of the above. SeeCREATE SECRETand the DuckDB Secrets Manager.
Airport vs ADBC Scanner
Both extensions stream Arrow from a remote system end-to-end, but they target different worlds — pick by what’s on the other end of the wire:
- Use Airport when the upstream speaks Arrow Flight — typically custom services, Flight-native systems, or federation gateways. Flight is an RPC protocol.
- Use
adbc_scannerwhen the upstream is a database with an ADBC driver — SQLite, PostgreSQL, Snowflake, BigQuery, etc. ADBC is a client API standard, not a wire protocol. - Both can coexist in the same DuckDB session: Airport for Flight-native services,
adbc_scannerfor traditional databases, all queryable side by side.
Background: what is Arrow Flight?
Arrow Flight is the Apache Arrow project’s wire protocol for moving Arrow data over the network. It runs on gRPC over HTTP/2 and uses Arrow’s columnar IPC format end-to-end, so result batches arrive on the client in the same layout the server produced them — no row/column transposition. The protocol’s standard verb vocabulary — ListFlights, GetFlightInfo, DoGet, DoPut, DoExchange, DoAction — is what Airport translates DuckDB SQL into. The full design rationale for Airport and server implementation guides live at airport.query.farm.
Install
INSTALL airport FROM community;
LOAD airport;
Quick Start
Attach a remote Arrow Flight database
-- `scope` matches the secret to the URL you ATTACH against.
CREATE SECRET my_flight (
TYPE airport,
scope 'grpc+tls://flight.example.com:8815',
auth_token 'your-token'
);
ATTACH 'grpc+tls://flight.example.com:8815' AS remote (TYPE airport);
Query it as if it were local
CREATE SECRET my_flight (
TYPE airport,
scope 'grpc+tls://flight.example.com:8815',
auth_token 'your-token'
);
ATTACH 'grpc+tls://flight.example.com:8815' AS remote (TYPE airport);
SELECT * FROM remote.public.events LIMIT 100;
Inspect available flights from a server
SELECT * FROM airport_flights('grpc+tls://flight.example.com:8815');
Reference
Extension Contents
Quick reference to all available functions and settings organized by category.
| Name | Type | Description |
|---|---|---|
|
Arrow Flight Catalog
|
||
| airport | Attach an Arrow Flight server as a DuckDB catalog. | |
|
Arrow Flight Connection
|
||
| airport | Connection details for an Arrow Flight server. | |
|
Diagnostics
Identify the Airport extension version and the User-Agent it advertises to remote servers. Useful for debugging connectivity issues and matching server logs to a specific extension build. |
||
| airport_user_agent() | Returns the User-Agent header string Airport sends with Arrow Flight requests. | |
| airport_version() | Returns the version string of the loaded Airport extension. | |
|
Discovery
Enumerate what an Arrow Flight server exposes — its databases, its flights (each with a schema and metadata). Use these before ATTACH to figure out what's available. |
||
| airport_databases() | Lists databases exposed by the Arrow Flight server identified by the given secret. | |
| airport_flights() | Lists the flight descriptors offered by an Arrow Flight server, with their schemas and metadata. | |
|
Operations
Execute work against an Arrow Flight server. Stream the result of a specific ticket inline ( |
||
| airport_action() | Invokes a custom Flight Action on the server (the Arrow Flight RPC for non-query operations). | |
| airport_take_flight() | Fetches the result of a specific Arrow Flight ticket as a DuckDB table. | |
No extension contents match that search.
API Reference
Function Documentation
Database Storage
Storage Extensions
Catalog implementations that attach external storage as a DuckDB database.
airport
Description
Attach an Arrow Flight server as a DuckDB catalog. Once attached, schemas, tables, and SQL operations on the remote server become accessible like any local database — joins, filters, aggregates, and (when the server supports the corresponding Flight RPCs) INSERT / UPDATE / DELETE. Pass the Flight URL as the database identifier; the matching airport secret (if any) is picked up automatically by URL scope. The optional location ATTACH parameter overrides the URL when you want a logical name as the database identifier.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
location
|
VARCHAR
|
Optional |
Optional Arrow Flight server URI override (e.g. grpc://host:8815 or grpc+tls://host:8815). When omitted, the URL passed as the database identifier in ATTACH '<url>' AS <alias> is used.
|
auth_token
|
VARCHAR
|
Optional |
Bearer token sent in the gRPC Authorization header. Recommended via CREATE SECRET rather than inline so it's not stored in query text.
|
Examples
Attach by URL with credentials picked up from a matching secret (recommended)
-- 1. Stash credentials once. `scope` is the URL prefix the secret applies to.
CREATE SECRET my_flight (
TYPE airport,
scope 'grpc+tls://flight.example.com:8815',
auth_token :token
);
-- 2. Attach the URL — the Secrets Manager picks up `my_flight`
-- automatically because the URL matches its scope.
ATTACH 'grpc+tls://flight.example.com:8815' AS remote (TYPE airport);
-- 3. Query as if it were local
SELECT user_id, COUNT(*) AS events
FROM remote.public.activity
WHERE ts >= CURRENT_DATE - 7
GROUP BY user_id;
Attach inline without a secret (good for ad-hoc connections to unauthenticated servers)
ATTACH 'grpc://localhost:8815' AS remote (TYPE airport);
SHOW TABLES FROM remote.public;
Mutate a remote table — works only when the Flight server implements DoPut for the operation
ATTACH 'grpc+tls://flight.example.com:8815' AS remote (TYPE airport);
INSERT INTO remote.public.events
SELECT user_id, event_type, ts
FROM staging.new_events;
UPDATE remote.public.profile
SET country = 'CA'
WHERE user_id = 12345;
Security
Secrets
DuckDB secrets for storing the credentials and keys used by the airport extension.
airport
Description
Connection details for an Arrow Flight server. Used by ATTACH to establish a database connection and by the discovery / operations functions when invoked with a secret reference. scope is the Flight URL the secret applies to (DuckDB Secrets Manager scope-matches against it).
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| Parameter scope | Type VARCHAR | Required Required |
Description
Arrow Flight server URI, e.g. grpc://host:8815 or grpc+tls://host:8815. The DuckDB Secrets Manager matches ATTACH / function calls to a secret by URL prefix against this value.
|
| Parameter auth_token | Type VARCHAR | Required Optional | Description Bearer token sent in the gRPC Authorization header. |
Examples
Create a secret for a TLS-protected Flight server
CREATE SECRET my_flight (
TYPE airport,
scope 'grpc+tls://flight.example.com:8815',
auth_token 'your-token'
);
Attach the database — the matching secret is picked up automatically by URL scope
ATTACH 'grpc+tls://flight.example.com:8815' AS remote (TYPE airport);
SELECT * FROM remote.public.events LIMIT 10;
Practical Examples
Cookbook
Real-world recipes and patterns for common use cases.
Try it against the live demo server
Query.Farm runs a public demo Flight server at grpc+tls://hello-airport.query.farm so you can exercise Airport without building anything. Attach it, switch to the static schema, and join across two of its tables:
-- Connect to a remote Arrow Flight serverATTACH 'hello' (TYPE AIRPORT, location 'grpc+tls://hello-airport.query.farm');
-- Switch to the static schemaUSE hello.static;
-- Query remote tables with standard SQL — notice the join across two remote tablesSELECT greeting || ', ' || first_name || ' welcome to Airport.' AS greetingFROM greetingsJOIN people ON people.language = greetings.languageLIMIT 3;The chat schema demonstrates writes against a Flight server too — auto-assigned identity, message inserts, and a recent-messages query in the same session:
-- Switch to the chat schemaUSE hello.chat;
-- Get your auto-generated identitySELECT * FROM identity;
-- Post a message to the 'airport' channelINSERT INTO messages (channel, message)VALUES ('airport', 'Hello from Airport!');
-- See all recent messagesSELECT sender, message, timestampFROM messagesWHERE channel = 'airport'ORDER BY timestamp DESCLIMIT 10;See airport.query.farm for the demo’s full schema reference and notes on the underlying Flight server implementation.
Attach a Flight Database
The most powerful way to use Airport: attach an Arrow Flight server as a DuckDB database via ATTACH and query it like any other catalog.
-- 1. Store credentials with CREATE SECRET. `scope` is the Flight URL-- the secret applies to — DuckDB matches the secret by URL prefix.CREATE OR REPLACE SECRET my_flight ( TYPE airport, scope 'grpc+tls://flight.example.com:8815', auth_token 'your-bearer-token');
-- 2. ATTACH the URL directly. The Secrets Manager picks up `my_flight`-- automatically because the URL matches its scope.ATTACH 'grpc+tls://flight.example.com:8815' AS remote (TYPE airport);
-- 3. Query as if it's local — joins, filters, aggregates all push down-- to the Flight server when supported.SELECT user_id, COUNT(*) AS eventsFROM remote.public.activityWHERE ts >= CURRENT_DATE - 7GROUP BY user_idORDER BY events DESCLIMIT 50;DuckDB’s full SQL surface works against remote tables — joins across local and remote catalogs, CTEs, window functions, materialized CTAS into local tables. Credentials live in the DuckDB Secrets Manager (see CREATE SECRET) so they’re never inlined into queries — the secret is matched against the ATTACH URL by its scope prefix.
Discover What a Server Offers
Before attaching, list the databases and flights a server exposes:
-- Show databases the server hosts. `airport_databases` takes the-- Flight URL — the same URL you'd pass to ATTACH.SELECT * FROM airport_databases('grpc+tls://flight.example.com:8815');-- Show all flights (schemas + metadata) under a server URLSELECT * FROM airport_flights('grpc+tls://flight.example.com:8815');-- Filter by descriptor patternSELECT * FROM airport_flights( 'grpc+tls://flight.example.com:8815', /* criteria = */ 'tenant=acme');The result is a table — pipe it into WHERE / LIMIT / joins to drill down without attaching. See airport_databases and airport_flights for the full signatures.
Stream a Specific Flight Inline
When you already have a flight descriptor and just want its rows once, skip ATTACH and call airport_take_flight directly:
SELECT *FROM airport_take_flight( 'grpc+tls://flight.example.com:8815', /* descriptor = */ '{"path":["sales","2026-04"]}')LIMIT 100;Useful for read-once jobs, exports, or wiring Flight responses into a CTAS.
Invoke a Server Action
Arrow Flight has a separate RPC channel called Actions for non-query operations — refreshing a materialized view, taking a snapshot, calling a stored procedure. Airport exposes them via airport_action:
-- Trigger a server-defined "refresh" action and capture the result bodySELECT *FROM airport_action( 'grpc+tls://flight.example.com:8815', /* action = */ 'refresh', /* body = */ '{"tenant":"acme","since":"2026-04-01"}');The full set of available actions is server-defined — see your Flight server’s documentation for the action vocabulary.
Mutate a Remote Table
Airport supports INSERT / UPDATE / DELETE against attached Flight databases when the server implements the corresponding writer endpoints (Flight’s DoPut):
INSERT INTO remote.public.eventsSELECT user_id, event_type, tsFROM staging.new_events;UPDATE remote.public.profileSET country = 'CA'WHERE user_id = 12345;Whether each operation is supported depends on the server — airport_databases typically reports per-database write capabilities.
Diagnostics
SELECT airport_version(); -- e.g. '0.4.2'SELECT airport_user_agent(); -- the User-Agent string Airport sendsUse airport_version and airport_user_agent to confirm what’s loaded and to match Flight server logs against a specific extension build when debugging.
Platform Support
Compatibility
Extension availability may vary by platform and DuckDB version. Check below to ensure this extension supports your environment before installation.
Quick Facts
Platforms
- Linux x86_64 aarch64
- Linux (musl) Not available
- macOS Intel Apple Silicon
- Windows x86_64
- WASM Not available
Compiled binary sizes
| Platform | Architecture | Size |
|---|---|---|
| Linux | x86_64 | 24.56 MB |
| Linux | aarch64 | 22.76 MB |
| macOS | Intel | 16.73 MB |
| macOS | Apple Silicon | 15.58 MB |
| Windows | x86_64 | 14.77 MB |
Compressed download size from the Haybarn extension repository.
DuckDB & Haybarn
Release calendar- DuckDB v1.5.5 Haybarn 1.5.5-rc1 Supported