Docker runs SurrealDB without installing the server on the host machine. This page covers starting it from the official image and choosing a version tag.
Running the SurrealDB server using Docker
To get started using Docker, you can use the latest tag. To view all the available versions and tags, or to use a specific tag visit the Docker Hub page. To start a server use the start command. In Docker, SurrealDB listens on port 8000 in all interfaces by default so that the host can connect to the container in the default bridge networking mode.
docker run --rm --pull always -p 8000:8000 surrealdb/surrealdb:latest startThe latest tag is the current stable release. SurrealDB 4.0 is in beta, and its image is tagged with the beta release, such as surrealdb/surrealdb:v4.0.0-beta.1. See Install the beta.
For local development, use the latest-dev image variant (i.e., docker run --rm --pull always -p 8000:8000 surrealdb/surrealdb:latest-dev start). This version includes a shell and package manager, allowing you to install tools and interact with the container's internals.
In order to persist data when the Docker instance is restarted or shut down, specify a Docker folder using the Docker -v command line argument, and use an on-disk storage engine in SurrealDB with a path inside that folder. The examples on this page use SurrealKV (surrealkv:), the on-disk engine in SurrealDB 4.0. Before SurrealDB 4.0, RocksDB (rocksdb:) was the recommended engine, and it takes the same paths. SurrealDB 4.0 removed it; see Moving a RocksDB database to SurrealKV.
mkdir mydata # Create a directory to store the database, owned by the current user
docker run --rm --pull always -p 8000:8000 --user $(id -u) -v $(pwd)/mydata:/mydata surrealdb/surrealdb:latest start surrealkv:/mydata/mydatabase.dbThe default logging level for the database server is info. To control the logging verbosity, specify the --log argument. The following command starts the database with debug level logging, resulting in more logs being output to the terminal. If extra verbosity is not needed, specify a lower level or simply remove the flag, which will default to the info level.
mkdir mydata # Create a directory to store the database, owned by the current user
docker run --rm --pull always -p 8000:8000 --user $(id -u) -v $(pwd)/mydata:/mydata surrealdb/surrealdb:latest start --log debug surrealkv:/mydata/mydatabase.dbConfiguring authentication
Authentication is enabled by default on SurrealDB, while the --unauthenticated flag can be used to opt out.
To set up access as an authenticated user, configure your initial root-level user by setting the --user and --pass command-line arguments.
The following command starts the database with a top-level user named root with a password set to secret. The root user will be persisted in storage, which means you don't have to include these two arguments the next time you start SurrealDB.
docker run --rm --pull always -p 80:8000 -v /mydata:/mydata surrealdb/surrealdb:latest start --user root --pass secret surrealkv:/mydata/mydatabase.dbThe path after surrealkv: is a path inside the container, and has to point into the directory where the volume is mounted, here /mydata. A single slash, as in surrealkv:/mydata/mydatabase.db, makes it an absolute path, which is what reaches the volume. A relative path such as surrealkv:mydatabase.db is resolved from the working directory of the container, which is its root directory /. The container user cannot write there, so the server fails to start with IO error: Permission denied. Before SurrealDB 4.0, RocksDB reported Failed to create RocksDB directory and Permission denied. See Absolute vs. relative paths for how the number of slashes after the engine name decides which kind of path it is.
An absolute path gives the same Permission denied error when the container user cannot write to the mounted directory. The image runs as the non-root user nonroot, with UID 65532. On Linux a bind-mounted directory keeps the owner it has on the host, so a directory owned by root is not writable from the container. This includes a directory that Docker created because it did not exist. Mount a directory that you own and pass --user $(id -u), as in the examples at the top of this page, or give the directory to the container user with sudo chown 65532 /mydata.
In order to change the default port that SurrealDB uses for web connections and from database clients you can use the Docker -p command line argument to tunnel the port to the internal SurrealDB port which SurrealDB is served on. The following command starts the database on port 80.
docker run --rm --pull always -p 80:8000 -v /mydata:/mydata surrealdb/surrealdb:latest start --user root --pass secret surrealkv:/mydata/mydatabase.dbAfter running the above command, you should see the SurrealDB server start up successfully.
docker run --rm --pull always -p 80:8000 -v /local-dir:/container-dir surrealdb/surrealdb:latest start --user root --pass secret surrealkv:/container-dir/mydatabase.dbINFO surrealdb::core::kvs::ds: Starting kvs store at absolute path surrealkv:/container-dir/mydatabase.db
INFO surrealdb::core::kvs::ds: Started surrealkv kvs store
INFO surrealdb::core::kvs::ds: This is a new SurrealDB instance. Initialising default namespace 'main' and database 'main'
INFO surrealdb::core::kvs::ds: Credentials were provided, and no root users were found. The root user 'root' will be created
INFO surrealdb::net: Started web server on 0.0.0.0:8000The server has no web page of its own. Opening http://localhost:8000 in a browser redirects to SurrealDB Studio on surrealdb.com, which can then connect to the server. To check that the server is running, request http://localhost:8000/health instead, which returns 200 with an empty body. Queries go to the HTTP and WebSocket endpoints, such as /sql and /rpc.
For details on the start command, and all of the available configuration options and arguments, view the start command documentation.
Run your first query
With the server container running and port 8000 published, connect to it with the surreal sql command from the same image. This starts a SurrealQL REPL against the server, using the credentials from the authentication step above.
docker run --rm --pull always -it surrealdb/surrealdb:latest sql --endpoint http://host.docker.internal:8000 --username root --password secret --namespace main --database main --pretty host.docker.internal resolves to the host on Docker Desktop for macOS and Windows. On Linux, add --add-host=host.docker.internal:host-gateway to the command, or use --network host and connect to http://localhost:8000. If you have the surreal binary installed on the host, you can connect with surreal sql --endpoint http://localhost:8000 and the same flags instead.
Create a record. There is no need to define the table first, because SurrealDB creates it on the first write.
CREATE person:tobie SET name = "Tobie", city = "London";[
{
city: 'London',
id: person:tobie,
name: 'Tobie'
}
]Select it back to confirm the round trip.
SELECT name, city FROM person;[
{
city: 'London',
name: 'Tobie'
}
]When you finish, exit the REPL with Ctrl+C. If you started the server with a mounted volume and the on-disk storage engine, the record persists across container restarts.
Running SurrealDB with Docker Compose
The Compose file below starts SurrealDB with a persistent volume, reads the root credentials from a .env file, and loads a schema file every time the server starts.
services:
surrealdb:
image: surrealdb/surrealdb:latest
ports:
- "8000:8000"
env_file:
- .env
volumes:
- surrealdb-data:/data
- ./schema.surql:/schema.surql:ro
command: start --import-file /schema.surql surrealkv:/data/app.db
volumes:
surrealdb-data:SURREAL_USER=root
SURREAL_PASS=secretSome details about this file:
Credentials: the server reads
SURREAL_USERandSURREAL_PASSfrom its own environment, so the.envfile supplies them without a--useror--passargument. This avoids${VARIABLE}substitution incommand, which Compose performs with the variables of the shell that runsdocker composeand the.envfile next tocompose.yaml, not with the variables inenv_file. An empty substitution creates a root user with an empty name.Named volume: from the SurrealDB 4.0 image,
/dataand/logsbelong to the container user (UID65532), so a new named volume mounted at/datais writable by the server. With an earlier image, a new named volume belongs toroot, and the server fails to start withPermission deniedunless the service also setsuser: root.--import-file: the file is imported on every start, not only the first one. It can also be set with theSURREAL_IMPORT_FILEenvironment variable.
Because the import runs on every start, write the file so that it can run again on a database that already has the schema:
DEFINE NAMESPACE IF NOT EXISTS app;
USE NS app;
DEFINE DATABASE IF NOT EXISTS main;
USE DB main;
DEFINE TABLE IF NOT EXISTS person SCHEMAFULL;
DEFINE FIELD IF NOT EXISTS name ON person TYPE string; A statement in the import file that fails, such as a DEFINE TABLE without IF NOT EXISTS for a table that already exists, is skipped without an error in the log, and the server starts normally. Use IF NOT EXISTS so that the file gives the same result on every start, or OVERWRITE for a definition that the file must always replace.
Using the command-line tools within Docker
The Docker container contains both the server, and the command line tools for importing, exporting, and querying a remote SurrealDB server.
docker run --rm --pull always surrealdb/surrealdb:latest helpThe result should look similar to the output below, confirming that the SurrealDB command-line tool was installed successfully.
Start, query, import and export SurrealDB from the command line
Usage: surreal [OPTIONS] <COMMAND>
Commands:
start Start the database server
import Import a SurrealQL script into an existing database
export Export an existing database as a SurrealQL script
version Output the command-line tool and remote server version information
sql Start an SQL REPL in your terminal with pipe support
mcp Start the MCP server over stdio for AI assistant integration
is-ready Check if the SurrealDB server is ready to accept connections [alias: isready]
validate Validate SurrealQL query files
fix Fix database storage issues
help Print this message or the help of the given subcommand(s)
Options:
-v, --version Print version
-j, --json Emit machine-readable JSON on stdout
-h, --help Print help
Logging:
...For details on the different commands available, visit the CLI tool documentation.
Running SurrealDB Enterprise
Available since: v3.3.0
SurrealDB Enterprise is published as a separate image, surrealdb/surrealdb-enterprise, whose entrypoint is the Enterprise server binary, so it takes the same commands as the Community image. It needs a licence key to start. The following command passes the key from the SURREAL_LICENSE_KEY variable in the current shell:
docker run --rm --pull always -p 8000:8000 -e SURREAL_LICENSE_KEY surrealdb/surrealdb-enterprise:latest start --user root --pass secret-e SURREAL_LICENSE_KEY with no value copies the variable from the shell that runs docker, which keeps the key out of the command line. To read the key from a mounted file instead, set SURREAL_LICENSE_KEY_FILE to the file's absolute path inside the container. The node validates the key against api.keygen.sh when it starts, so the container needs outbound network access.
Next steps
Query from your application with an SDK - each language guide starts with a connect-and-query walkthrough.
Try SurrealQL without a server in the Studio Sandbox.
Learn the query language, starting with the
SELECTstatement.