⌘K ctrl+k
Search Shortcut cmd + k | ctrl + k
Connect

Overview

Every DuckDB feature reachable from Rust starts with a Connection. Opening in-memory and file-backed databases, configuring a database instance with Config, opening read-only, pooling connections across threads, the crate's thread-safety guarantees, and closing a connection are all covered below.

Opening a Connection

A Connection is created with Connection::open_in_memory() for an in-memory database, or Connection::open(path) for a database file. Both return a Result, so the ? operator propagates any error:

use duckdb::{Connection, Result};

// In-memory database: nothing is persisted to disk.
let conn = Connection::open_in_memory()?;

// File-backed database: created if it does not exist.
let conn = Connection::open("my_database.duckdb")?;

For an in-memory database no data is persisted to disk, so all data is lost when the process exits. A file-backed database is created if the file does not exist. The extension may be .db, .duckdb, or anything else.

Configuring a Connection

To set DuckDB options when the database instance starts, build a Config and open the connection with Connection::open_with_flags() (or Connection::open_in_memory_with_flags()). Config uses a builder style: each method consumes the config and returns it, and each returns a Result because DuckDB validates the option:

use duckdb::{Config, Connection, Result};

let config = Config::default()
    .max_memory("4GB")?
    .threads(4)?;
let conn = Connection::open_with_flags("my_database.duckdb", config)?;

Config exposes typed methods for the most common options, including access_mode(), max_memory(), threads(), default_order(), default_null_order(), enable_external_access(), enable_object_cache(), custom_user_agent(), and allow_unsigned_extensions(). Any other DuckDB setting can be supplied by name with with():

let config = Config::default()
    .with("temp_directory", "/path/to/temp/dir/")?
    .with("preserve_insertion_order", "false")?;

The full list of settings is on the Configuration page. Many of them can also be changed after connecting with a SET statement or the equivalent PRAGMA.

Read-Only Connections

A database file can be opened in read-only mode, which is useful when several processes read the same file at once. Set the access mode on a Config with the AccessMode enum:

use duckdb::{AccessMode, Config, Connection, Result};

let config = Config::default().access_mode(AccessMode::ReadOnly)?;
let conn = Connection::open_with_flags("my_database.duckdb", config)?;

AccessMode has three variants: Automatic (the default), ReadOnly, and ReadWrite.

Thread Safety

A Connection is Send but not Sync: it can be moved to another thread, but it cannot be shared between threads at the same time. The recommended pattern is one connection per thread. To open several connections to the same in-memory or file-backed database, use Connection::try_clone(), which opens a new connection to the already-opened database:

let conn = Connection::open("my_database.duckdb")?;
let conn2 = conn.try_clone()?; // a second connection to the same database

DuckDB runs queries on its own pool of native threads, sized by the threads option and shared by all connections to a database instance. See the DuckDB concurrency documentation for how connections and threads interact.

Connection Pooling

For applications that hand a connection to each worker thread, the r2d2 feature integrates DuckDB with the r2d2 connection pool. A DuckdbConnectionManager creates connections that the pool checks out and returns:

use duckdb::{params, DuckdbConnectionManager};

let manager = DuckdbConnectionManager::file("my_database.duckdb")?;
let pool = r2d2::Pool::new(manager)?;

// Each worker checks out its own connection from the pool.
let conn = pool.get()?;
conn.execute("INSERT INTO foo (bar) VALUES (?)", params![1])?;

DuckdbConnectionManager::memory() builds a manager for an in-memory database, and DuckdbConnectionManager::file_with_flags() and memory_with_flags() accept a Config. Enable the feature in Cargo.toml with features = ["r2d2"].

Closing a Connection

A Connection closes automatically when it is dropped (goes out of scope), which shuts down the underlying database. To close it explicitly and handle any error, call close():

match conn.close() {
    Ok(()) => {}
    Err((_conn, err)) => eprintln!("failed to close connection: {err}"),
}

close() returns the connection back to the caller on failure, so closing can be retried. In the typical case there is little difference between an explicit close() and letting the connection drop, but close() gives a chance to observe and handle a shutdown error that Drop would otherwise discard.

Further Reading

  • Run Queries — using the Connection to send queries and read results.
  • Configuration — the full list of DuckDB settings that can be passed to Config.
  • Concurrency — how DuckDB handles multiple connections and threads.
  • Troubleshoot — linking and build problems encountered when opening a connection.
© 2026 DuckDB Foundation, Amsterdam NL
DuckDB Home Code of Conduct Trademark Use Blog