sqlcmd command not found in SQL Server 2022 Docker container

Summary

The user is unable to run sqlcmd inside the official SQL Server 2022 Docker container because the base image (mcr.microsoft.com/mssql/server:2022-latest) does not include the mssql-tools package by default. This is a deliberate design choice to keep the database server image lean and focused on the engine. Additionally, attempting to install the tools using apt-get fails because the SQL Server Linux images are based on Ubuntu, but the apt package repositories are typically disabled or missing in the minimal runtime layers to optimize image size and security.

Root Cause

The root cause is a misunderstanding of the contents of the official Microsoft SQL Server Docker images.

  1. Separation of Concerns: Microsoft separates the database engine (mssql/server) from the client tools (mssql-tools).
  2. Minimalist Image Design: The mssql/server image contains only the SQL Server engine and necessary dependencies. Tools like sqlcmd and bcp are packaged separately in the mssql-tools image or package, which is not included in the base server image.
  3. Restricted Environment: The SQL Server Linux container environment is minimal. While it is based on Ubuntu, many system package management tools and repositories are removed or restricted to prevent modification and reduce the attack surface. Consequently, apt-get is not available or functional, making it impossible to install packages directly inside the running container via standard means.

Why This Happens in Real Systems

In production engineering, container images are optimized for specific roles.

  • Image Size and Security: Including development and administration tools (like sqlcmd) in a production database image increases the container size and potential attack surface. In a hardened production environment, access to a shell inside the container is often restricted, and management is performed externally.
  • Ephemeral Containers: Containers are designed to be immutable and ephemeral. If a tool is needed, it should ideally be added during a custom build process (creating a new image layer) rather than installed manually on a running instance.
  • Dependency Hell: Relying on apt-get inside a runtime container can lead to version conflicts or broken dependencies if the repositories are out of sync with the locked base OS version of the image.

Real-World Impact

  • Operational Blockers: Administrators cannot connect to the database using the standard command-line interface (sqlcmd) to perform maintenance tasks, such as restoring backups (.bak files), running scripts, or checking server status directly from the host via docker exec.
  • Inefficient Workarounds: Teams often waste time trying to “hack” the container by installing tools, which violates the immutable infrastructure principle and can lead to unstable or non-reproducible environments.
  • Deployment Delays: Without sqlcmd, automating database initialization or migration steps in CI/CD pipelines requires finding alternative drivers or containers, complicating the workflow.

Example or Code

To verify the issue and the environment constraints, one might run the following commands.

Checking for sqlcmd (which fails):

docker exec -it sqlserver /opt/mssql-tools/bin/sqlcmd

Result: No such file or directory

Checking for apt-get (which fails):

docker exec -it sqlserver apt-get update

Result: bash: apt-get: command not found

The Correct Approach (Mounting Tools):
Instead of installing tools inside the DB container, use a sidecar container or a volume mount with the tools image to interact with the DB container.

# Run a separate container with mssql-tools that shares the network with the sqlserver container
docker run --rm -it \
  --network host \
  -e PGPASSWORD='youdontneedtoknow' \
  mcr.microsoft.com/mssql-tools \
  /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P 'youdontneedtoknow' -Q "SELECT @@VERSION;"

How Senior Engineers Fix It

Senior engineers adhere to the separation of infrastructure and management tooling.

  1. Use the Dedicated Tools Image: Use the mcr.microsoft.com/mssql-tools image. It is lightweight and contains sqlcmd and bcp pre-installed. Run it as a sidecar container communicating with the SQL Server container via Docker networking.

  2. Custom Dockerfile (Build Time): If you absolutely require sqlcmd inside the same container (e.g., for a specific startup script), create a custom Dockerfile. Crucially, you must enable the package repositories and install dependencies during the build phase, not the run phase.

    Example Dockerfile snippet:

    FROM mcr.microsoft.com/mssql/server:2022-latest
    
    # Switch to root to install tools
    USER root
    
    # Enable repositories and install mssql-tools (Ubuntu specific)
    # Note: This requires the official Microsoft apt repo keys and sources list
    RUN apt-get update && \
        apt-get install -y mssql-tools && \
        apt-get clean && \
        rm -rf /var/lib/apt/lists/*
    
    # Switch back to the non-root user defined in the base image
    USER mssql
  3. Host Mapping: For simple manual tasks, you can mount the mssql-tools binaries from your host machine into the container if you have them installed locally, though networking a sidecar container is the preferred cloud-native method.

Why Juniors Miss It

  • Assumption of Linux Norms: Junior engineers often assume that any Linux-based container acts like a standard Linux server (like an EC2 instance or VPS) where apt-get is universally available. They don’t realize that production container images are often stripped down.
  • Lack of Awareness of Image Variants: They might not know that Microsoft publishes a separate mssql-tools image. They look for tools in the main server image and assume they are just missing or corrupted.
  • Confusion with “Runtime” vs. “Build”: They try to modify a running container (runtime) to add features, rather than building a new image (build time) that includes those features. This violates the concept of immutable infrastructure.