# go-sqlcmd wants to be two tools, and the context file is where they meet

> go-sqlcmd is Microsoft's Go port of the ODBC sqlcmd, and it also adds a container aware workflow for creating local SQL Server instances with sqlcmd create, sqlcmd query and sqlcmd open ads. The port keeps the old flags with a handful of deliberate differences.

**microsoft/go-sqlcmd** — The new sqlcmd, CLI for SQL Server and Azure SQL (winget install sqlcmd / sqlcmd create mssql / sqlcmd open ads)

- Repository: https://github.com/microsoft/go-sqlcmd
- Website: https://learn.microsoft.com/sql/tools/sqlcmd/go-sqlcmd-utility
- Stars: 611 · Forks: 92
- Language: Go
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/microsoft-go-sqlcmd

## A Go port of ODBC sqlcmd, plus a container workflow on top

go-sqlcmd holds the sqlcmd command line tool and Go packages for working with Microsoft SQL Server, Azure SQL Database and Azure Synapse. It is written in Go, and the README states the aim plainly: a complete port of the original ODBC sqlcmd to the Go language, using the go-mssqldb driver.

The word complete is doing work there, and it is qualified later. The tool keeps the familiar query interface, the same -Q and -q style flags, and the ability to pipe SQL in. What it adds is a layer that the old tool never had: the ability to create, start, stop and delete local SQL Server instances using a local container runtime such as Docker or Podman, through subcommands like sqlcmd create mssql, sqlcmd query and sqlcmd open ads.

That is a genuinely different tool from the one it replaces, and it explains the version numbering. The recent releases are v1.10.0 on 2026-03-03, v1.9.0 on 2025-07-17 and v1.8.2 on 2024-12-12, so this is a young, still moving project with a fast release cadence at times.

The homepage is the Microsoft Learn page for the go-sqlcmd utility, and the repository has a SECURITY.md and a SUPPORT.md, which is a reasonable signal for a tool from a large vendor that people are expected to run against production databases.

## Installing it on Windows, macOS and Linux

sqlcmd is available through the package managers for all major platforms, and the README gives the exact commands for each.

On Windows it is available via Winget and Choco, and as a downloadable .msi or .zip from the releases page. The Winget install is `winget install sqlcmd` with `winget upgrade sqlcmd` for upgrades, and the Choco equivalent is `choco install sqlcmd` with `choco upgrade sqlcmd`.

The .msi installer is signed with a Microsoft Authenticode certificate, which is worth knowing if your Windows environment has installation policies that reject unsigned binaries.

On macOS it comes through Homebrew with `brew install sqlcmd` and `brew upgrade sqlcmd`, or as a downloadable .tar from the releases page.

On Linux it is available via Linuxbrew and as a downloadable .rpm, .deb and .tar, and the README also notes it is available through apt-get, yum and zypper. Homebrew can be used on Linux and on Windows Subsystem for Linux version 2.

One platform note is specific and worth planning for. Macs running Apple Silicon require Docker Desktop to use Rosetta for x86/amd64 emulation, and the README lists the steps: open Docker Desktop, go to the settings or preferences menu, find the Features in development section, and enable the Use Rosetta for x86/amd64 emulation on Apple Silicon checkbox. That has to be done before creating a SQL Server instance on an M1 or M2 machine, which tells you the container images it pulls are still amd64.

## sqlcmd create gives you a local instance from a container runtime

The feature that has no equivalent in the old tool is instance management. The README says to use sqlcmd to create SQL Server instances using a local container runtime, for example Docker or Podman.

The example creates a local SQL Server instance with the AdventureWorksLT database restored, queries it, and connects to it using Azure Data Studio.

```bash
sqlcmd create mssql --accept-eula --using https://aka.ms/AdventureWorksLT.bak
sqlcmd query "SELECT DB_NAME()"
sqlcmd open ads
```

That is a complete loop in three commands. A database is created with a sample dataset, a query runs against it, and a graphical client opens on the same context.

The help model is documented in one line: sqlcmd --help lists the available sub-commands, and sqlcmd -? lists the original ODBC sqlcmd flags. Two different help systems in one binary, and the distinction between them is the main thing to internalise before writing scripts against this tool.

Instance lifecycle has its own verbs. To stop, start and delete contexts, the commands are sqlcmd stop, sqlcmd start and sqlcmd delete.

```bash
sqlcmd stop
sqlcmd start
sqlcmd delete
```

## Contexts, the sqlconfig file, and switching endpoints

Every time sqlcmd create completes, a new context is created, with names like mssql, mssql2 and mssql3. A context holds the endpoint and the user configuration detail, and the set of contexts lives in a file at ~/.sqlcmd/sqlconfig.

Switching between them is done with sqlcmd config use and a context name, while sqlcmd config current-context shows which one is active and sqlcmd config get-contexts lists them all.

```bash
sqlcmd config connection-strings
sqlcmd config view
```

Those two commands are how you see what the tool knows. The first shows connection strings in ODBC, ADO.NET, JDBC and other formats for the current context, and the second shows user and endpoint details for all the contexts in the file. That matters for scripting, because it is how you find out which server a script will actually hit.

The configuration file can be relocated. The --sqlconfig flag specifies a custom configuration file, the file must be YAML, and extensionless filenames are allowed while a file with an extension must be .yaml or .yml.

```bash
sqlcmd config --sqlconfig ./myproject.yaml add-endpoint --name ep1434 --address localhost --port 1434
sqlcmd config --sqlconfig ./myproject.yaml view
```

So a project can carry its own endpoint definitions in version control, which is the piece that makes the tool usable in CI as well as on a laptop.

## Picking a SQL Server version tag for your instance

Instances are not created from an implicit latest. The README describes a versions workflow where you list the available version tags, for example 2017, 2019 and 2022, and install a specific one.

```bash
SET SQLCMD_ACCEPT_EULA=YES

sqlcmd create mssql get-tags
sqlcmd create mssql --tag 2019-latest
```

The environment variable is set first because the container images require the licence to be accepted. The first command lists the tags, and the second creates the instance with the chosen one.

This is a sensible design for a tool that starts servers on a developer machine or in CI. Pinning a tag means the instance you get today matches the one you get next quarter, and a workflow that must reproduce a bug report can name the version explicitly rather than relying on whatever the registry serves.

The cost is that the tag list is a moving target. The README does not say how often new tags appear or how they are retired, so an automation that pins a tag should be prepared for that tag to disappear and for the pinned value to become the thing you have to change.

## Piping SQL, and the one behaviour that differs from the ODBC tool

For scripting, the README shows SQL piped directly to sqlcmd from the command line, and gives both a PowerShell and a Bash form.

```bash
echo "SELECT @@version" | sqlcmd -S myserver -d mydb -G
cat myscript.sql | sqlcmd -S myserver -d mydb -G
```

```powershell
"SELECT @@version" | sqlcmd -S myserver -d mydb -G
```

One behaviour note applies to both. The README says that when piping input, GO batch terminators are optional, because sqlcmd automatically executes the batch when the input ends, and that you can still include GO statements if you want to execute multiple batches.

That is a small convenience with a small trap. A script that relied on GO to split statements will still work, but a script that accidentally contains the word GO in a string literal could be split earlier than the old tool would have split it. Worth a look at any script you port.

For full backward compatibility with the ODBC tool, the README explains that connecting to the current context and using the original ODBC flags such as -q, -Q, -i and -o is done by invoking sqlcmd with those flags.

```bash
sqlcmd -q "SELECT @@version"
sqlcmd
```

And if no current context exists, sqlcmd with no connection parameters reverts to the original ODBC sqlcmd behaviour of creating an interactive session to the default local instance on port 1433 using trusted authentication. Otherwise it creates an interactive session to the current context. That fallback is the safety net that lets old scripts keep working on a machine that has never run sqlcmd create.

## The flag changes that will break a ported script

The README has an explicit section on changes in behaviour from the ODBC based sqlcmd, and two of them are worth memorising before you migrate anything.

First, the slash is not accepted as a flag specifier, only the hyphen. Any script using /i or /q has to change.

Second, there are new posix-style versions of each flag, such as --input-file for -i, and sqlcmd -? prints those parameter names. The README is explicit that the new names do not preserve backward compatibility with ODBC sqlcmd, and gives a concrete example: to specify multiple input file names with --input-file, the file names must be comma-delimited rather than space-delimited. A script that spaces its file names will quietly behave differently, because with the old flag the space form was how you passed several files.

There are also three switches with changed behaviour. The -R switch is ignored, because the Go runtime does not provide access to user locale information and it is not readily available through syscall on all supported platforms. The -I switch is ignored and quoted identifiers are always on, with SET QUOTED_IDENTIFIER OFF in your scripts as the documented way to turn that off. And -N now takes an optional string value from a fixed set of encryption choices, s for strict, t for true, m for mandatory, yes, 1, o for optional, no, 0, f for false, or disable, with true used if -N is passed with no value.

Together these are the migration checklist: a script that passes a locale, relies on quoted identifier off by default, or spaces multiple input file names will need editing.

## MIT licensed, actively released, and still Go 1.26 only

The licence is MIT, with the text in the LICENSE file and a NOTICE.md alongside it, and the Dockerfile carries a copyright header for Microsoft Corporation. The repository is not archived and the last push was on 2026-10-15, so the project is being worked on.

The release numbering is where the cost of adoption shows up. v1.9.0 in July 2025, v1.10.0 in March 2026, and v1.8.2 in December 2024, so minor versions carry behaviour changes, and the changes listed in the README include flag semantics that alter how scripts run. Upgrading is not a version bump in the usual sense, it is a re-read of the behaviour list.

The go.mod sets the module to github.com/microsoft/go-sqlcmd with go 1.26 and a toolchain of go1.26.6, so building from source needs a current Go. The dependency list shows the two halves of the tool: microsoft/go-mssqldb for the database connection, and the moby client and docker distribution packages for the container work. cobra and viper provide the command structure and configuration, peterh/liner supplies the interactive line editor, alecthomas/chroma handles syntax highlighting in the terminal, and billgraziano/dpapi is there for Windows credential protection. Azure SDK packages appear as indirect dependencies, which is how the Azure SQL and Synapse paths are reached.

The container image is minimal. The Dockerfile is built FROM scratch, copies the sqlcmd binary to /usr/bin/sqlcmd, and sets the command to sqlcmd, with the example invocation in a comment being docker run --rm microsoft/sqlcmd sqlcmd --help.

## Conclusion

Adopt go-sqlcmd if you want a single cross platform CLI for SQL Server and Azure SQL that also spins up local instances from a container runtime, and you are prepared to learn the new subcommand structure. Do not adopt it if your scripts depend on the exact ODBC sqlcmd flag semantics, because the port changes the flag specifier and adds long forms that are not backward compatible. Before you migrate, run sqlcmd -? against the ODBC tool to diff the flags you actually use, and read the changed behaviour list in the README before assuming a script still works unchanged.

## FAQ

### How do I install the new sqlcmd?

Through Winget with winget install sqlcmd, Choco with choco install sqlcmd, or Homebrew with brew install sqlcmd. On Linux it is also available via apt-get, yum and zypper, and as a downloadable .rpm, .deb or .tar from the releases page.

### How do I create a local SQL Server instance with go-sqlcmd?

Use sqlcmd create mssql with a local container runtime such as Docker or Podman. The README example restores the AdventureWorksLT database with --using, and you can then run sqlcmd query and sqlcmd open ads to query it and open Azure Data Studio.

### What is a context in sqlcmd and where is it stored?

Each sqlcmd create run makes a new context holding the endpoint and user configuration, and the contexts live in the ~/.sqlcmd/sqlconfig file. You switch with sqlcmd config use, list them with sqlcmd config get-contexts, and inspect them with sqlcmd config view.

### Is go-sqlcmd compatible with the old ODBC sqlcmd?

Not exactly. The README notes that slash is not accepted as a flag specifier, that the new posix-style long flags do not preserve backward compatibility, that -R and -I are ignored, and that -N now takes an encryption value. Use sqlcmd -? to list the flags of the version you have.

### Can I pipe SQL scripts into go-sqlcmd?

Yes. The README shows echo and cat piped into sqlcmd with server, database and authentication flags, in both Bash and PowerShell form. When input is piped, GO batch terminators are optional because sqlcmd executes the batch when the input ends, though GO statements still work for multiple batches.

## Sources

- [License: MIT](https://github.com/microsoft/go-sqlcmd/blob/main/LICENSE)
- [microsoft/go-sqlcmd on GitHub](https://github.com/microsoft/go-sqlcmd)
- [Project website](https://learn.microsoft.com/sql/tools/sqlcmd/go-sqlcmd-utility)
- [README](https://github.com/microsoft/go-sqlcmd/blob/main/README.md)
- [Releases](https://github.com/microsoft/go-sqlcmd/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/microsoft-go-sqlcmd
