To read a Feather file from the command line, use clickhouse local. It runs SQL directly on files from the command line, with no server to install. It's part of ClickHouse, so the same query scales to billions of rows when you outgrow your laptop.
Install it with clickhousectl:
curl https://clickhouse.com/cli | sh # install clickhousectl
clickhousectl local use latest # download ClickHouse and put it on your PATH
Then query the file directly:
clickhouse local -q "SELECT * FROM file('events.feather') LIMIT 10"
┌──────────────event_time─┬─event_id─┬─country─┬─event_type─┬─revenue─┬─quantity─┐
1. │ 2026-01-01 00:00:00.000 │ 1 │ GB │ click │ 5 │ 1 │
2. │ 2026-01-01 01:00:00.000 │ 2 │ US │ view │ 6.01 │ 2 │
3. │ 2026-01-01 02:00:00.000 │ 3 │ DE │ purchase │ 7.02 │ 3 │
4. │ 2026-01-01 03:00:00.000 │ 4 │ FR │ refund │ 8.03 │ 4 │
5. │ 2026-01-01 04:00:00.000 │ 5 │ IN │ click │ 9.04 │ 5 │
└─────────────────────────┴──────────┴─────────┴────────────┴─────────┴──────────┘
Feather V2 is the Arrow IPC file format, so ClickHouse recognizes the .feather extension automatically and reads the file in place with no import step.
This is the one thing to know. "Feather" is not a separate format with its own reader. Feather V2 is the on-disk Arrow IPC file format, the same columnar layout the Arrow project uses for files. A .feather file written by pandas, pyarrow, R, or Polars is an Arrow IPC file with a different extension.
So ClickHouse reads it with FORMAT Arrow. The extension is detected from the file name, but you can also say it outright:
clickhouse local -q "SELECT count() FROM file('events.feather', 'Arrow')"
Both forms read the same file. If you ever rename a .feather to something ClickHouse doesn't recognize, pass 'Arrow' as the second argument and it reads exactly the same.
Arrow files carry their own schema, so you never write CREATE TABLE. DESCRIBE prints the column names and the types ClickHouse read from the file:
clickhouse local -q "DESCRIBE file('events.feather') FORMAT PrettyCompact"
┌─name───────┬─type─────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
1. │ event_time │ DateTime64(3, 'UTC') │ │ │ │ │ │
2. │ event_id │ UInt64 │ │ │ │ │ │
3. │ country │ String │ │ │ │ │ │
4. │ event_type │ String │ │ │ │ │ │
5. │ revenue │ Float64 │ │ │ │ │ │
6. │ quantity │ UInt8 │ │ │ │ │ │
└────────────┴──────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
The types come straight from the file's Arrow schema: the timestamp lands as DateTime64, the integers keep their widths, and the floats stay Float64. (The extra empty columns are CREATE TABLE metadata, like defaults, codecs and TTLs, which an Arrow file doesn't carry.)
Filter, aggregate, and group by
A viewer shows you rows. A query engine answers questions. Because the file is just a SQL source, you have the full ClickHouse dialect (WHERE, GROUP BY, aggregate functions, window functions, joins):
clickhouse local -q "
SELECT country,
count() AS events,
round(sum(revenue), 2) AS revenue,
round(avg(quantity), 3) AS avg_qty
FROM file('events.feather')
WHERE event_type = 'purchase'
GROUP BY country
ORDER BY revenue DESC
FORMAT PrettyCompact"
┌─country─┬─events─┬─revenue─┬─avg_qty─┐
1. │ GB │ 2 │ 34.24 │ 3 │
2. │ DE │ 2 │ 26.16 │ 4 │
3. │ IN │ 1 │ 15.1 │ 1 │
└─────────┴────────┴─────────┴─────────┘
Arrow is columnar, so a query that touches a few columns reads only those columns. The rest are never decoded.
There are two things people call "Feather". Feather V2 is the Arrow IPC format, and it is what every current tool writes by default; its files begin with the magic bytes ARROW1. There is also a legacy Feather V1 format from 2016, which predates the Arrow IPC spec and starts with FEA1. They are different on-disk layouts.
ClickHouse's Arrow reader handles the Arrow IPC format (V2). Hand it a legacy V1 file and it tells you plainly:
clickhouse local -q "SELECT * FROM file('events_v1.feather', 'Arrow')"
Code: 636. DB::Exception: The table structure cannot be extracted from a Arrow format file. Error:
Code: 1002. DB::Exception: Error while opening a table: Invalid: Not an Arrow file. (UNKNOWN_EXCEPTION)
If you hit this, you have an old V1 file. Re-save it as the default (V2) in whatever tool produced it (for example pyarrow.feather.write_feather(table, "out.feather") writes V2) and ClickHouse reads it. In practice almost every .feather you meet today is already V2.
Feather and Parquet are both columnar Arrow-adjacent formats, but they trade off differently: Feather (Arrow IPC) is optimized for fast read/write and zero-copy interchange between tools, while Parquet compresses harder for long-term storage. Converting between them is one command: SELECT from the Feather file, INTO OUTFILE as Parquet:
clickhouse local -q "SELECT * FROM file('events.feather') INTO OUTFILE 'events.parquet' TRUNCATE FORMAT Parquet"
Read it back with the same file() call. See how to query a Parquet file for the typing and compression options.
Small files are instant in anything. The difference shows up at scale. On a 3,000,000-row, ~77 MB Feather file (events_large.feather, built by the example folder below), the same filter-and-group-by query runs in:
clickhouse local --time -q "
SELECT country, count(), round(sum(revenue), 2), round(avg(quantity), 3)
FROM file('events_large.feather')
WHERE event_type = 'purchase'
GROUP BY country ORDER BY 3 DESC
FORMAT Null"
~0.07 seconds of query execution (the --time flag reports the query time, not process startup), best of three with the file warm in the OS page cache, on an Apple M4 Pro laptop (14 cores, 24 GB RAM; clickhouse local 26.6.1.117). End to end, including launching the binary, the same run finishes in about 0.2 seconds. Arrow stores columns contiguously, so the scan reads only the four columns the query needs and runs across all cores.
clickhouse local reads Feather from the command line with one binary that also reads Parquet, CSV, JSON, ORC and many other formats, talks to S3, MySQL and Postgres, and runs the same SQL unchanged when you move from a file to a server to the Cloud.
The query you just ran on a laptop file is the same SQL you would run on a ClickHouse server, or in ClickHouse Cloud. Nothing about SELECT ... WHERE ... GROUP BY changes. You swap file('events.feather') for a table name and the rest stays put. You prototype against a file on your machine and ship the identical logic to production.
The complete, runnable example lives here. It has generate.sh (builds the demo file, a legacy V1 file, and the ~77 MB perf file), run.sh (every command above), and expected_output.txt:
github.com/ClickHouse/examples → local-analytics/clickhouse-local-feather
git clone https://github.com/ClickHouse/examples
cd examples/local-analytics/clickhouse-local-feather
./generate.sh && ./run.sh