Airport
ATTACH an Arrow Flight server as a DuckDB catalog and query it in SQL. Best fit: custom Flight services you build in Python / Go / Java, plus Flight-native systems like Dremio and InfluxDB 3. Capabilities (pushdown, mutation, schema discovery) follow what your Flight server implements.
Install
-- Install the extension
INSTALL airport FROM community;
-- Load it into your session
LOAD airport;
-- 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 as if it were local
SELECT * FROM remote.public.events LIMIT 100;
-- Inspect available flights from a server
SELECT * FROM airport_flights('grpc+tls://flight.example.com:8815'); 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 events
FROM svc.public.activity
WHERE ts >= CURRENT_DATE - 7
GROUP 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.
Reference
Extension Contents
Quick reference to all available functions and settings organized by category.
| Name | 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 | |
API Reference
Function Documentation
Detailed documentation for each function including signatures, parameters, and examples.
airport_action
Signature
Parameters
| Parameter | Type | Mode | Description |
|---|---|---|---|
col0 | VARCHAR | Positional | |
col1 | VARCHAR | Positional | |
headers | MAP(VARCHAR, VARCHAR) | Named | |
secret | VARCHAR | Named | |
auth_token | VARCHAR | Named |
Returns
Description
Invokes a custom Flight Action on the server (the Arrow Flight RPC for non-query operations). Used for server-defined commands like refresh, snapshot, or stored-procedure calls.
Examples
Related Functions
airport_databases
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
server_location | ANY | Positional |
Returns
Description
Lists databases exposed by the Arrow Flight server identified by the given secret. Used to enumerate what's available before attaching.
Examples
Related Functions
airport_flights
Signature
Parameters
| Parameter | Type | Mode | Description |
|---|---|---|---|
col0 | VARCHAR | Positional | |
secret | VARCHAR | Named | |
auth_token | VARCHAR | Named |
Returns
Description
Lists the flight descriptors offered by an Arrow Flight server, with their schemas and metadata. The starting point for ad-hoc querying without ATTACH.
Examples
Related Functions
airport_take_flight
Signature
Parameters
| Parameter | Type | Mode | Description |
|---|---|---|---|
col0 | POINTER | Positional | |
col1 | POINTER | Positional | |
col2 | VARCHAR | Positional | |
at_value | ANY | Named | |
at_unit | VARCHAR | Named | |
secret | VARCHAR | Named | |
auth_token | VARCHAR | Named |
Returns
Description
Fetches the result of a specific Arrow Flight ticket as a DuckDB table. Use when you have a known flight descriptor and want to stream its results inline.
Examples
Related Functions
airport_user_agent
Signature
Parameters
| Parameter | Type | Mode | Description |
|---|
Returns
Description
Returns the User-Agent header string Airport sends with Arrow Flight requests.
Examples
airport_version
Signature
Parameters
| Parameter | Type | Mode | Description |
|---|
Returns
Description
Returns the version string of the loaded Airport extension.
Examples
Related Functions
Database Storage
Storage Extensions
Storage extensions that enable secure database attachments with encryption, secret management, and credential rotation.
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`](#secrets) 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 securely storing credentials and encryption 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 |
|---|---|---|---|
| scope | VARCHAR | Required | 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. |
| auth_token | VARCHAR | Optional | 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 server
ATTACH 'hello' (TYPE AIRPORT, location 'grpc+tls://hello-airport.query.farm');
-- Switch to the static schema
USE hello.static;
-- Query remote tables with standard SQL — notice the join across two remote tables
SELECT greeting || ', ' || first_name || ' welcome to Airport.' AS greeting
FROM greetings
JOIN people ON people.language = greetings.language
LIMIT 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 schema
USE hello.chat;
-- Get your auto-generated identity
SELECT * FROM identity;
-- Post a message to the 'airport' channel
INSERT INTO messages (channel, message)
VALUES ('airport', 'Hello from Airport!');
-- See all recent messages
SELECT sender, message, timestamp
FROM messages
WHERE channel = 'airport'
ORDER BY timestamp DESC
LIMIT 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 events
FROM remote.public.activity
WHERE ts >= CURRENT_DATE - 7
GROUP BY user_id
ORDER BY events DESC
LIMIT 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 URL
SELECT * FROM airport_flights('grpc+tls://flight.example.com:8815');
-- Filter by descriptor pattern
SELECT * 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 body
SELECT *
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.events
SELECT user_id, event_type, ts
FROM staging.new_events;
UPDATE remote.public.profile
SET 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 sends
Use 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
Supported platform architectures
Compiled binary sizes
| Platform | Architecture | Size |
|---|---|---|
| Linux | aarch64 | 22.63 MB |
| Linux | x86_64 | 24.47 MB |
| Linux (musl) | x86_64 | 25.33 MB |
| macOS | Apple Silicon | 15.61 MB |
| macOS | Intel | 16.78 MB |
| Windows | x86_64 | 14.97 MB |
Gzipped download size from the DuckDB community-extensions registry.
SQL access to your Arrow Flight service
Install Airport, point it at a Flight server, and DuckDB queries it like any other catalog — to the extent the server supports the operations you need.