Workspace and Packages

Namespace brings a modern package management approach to infrastructure development. All the user code is organized into modules that define the versioning boundary. Modules in turn, are further split into packages that represent individual servers and/or collect tests, definitions of secrets, resources and volumes.


A workspace defines a set of environments and their configurations. ns-workspace.cue contains configuration bits that need to be shared between developers, for example:

// See below for more details about environments.
environment: {
	dev: {
		runtime: "kubernetes"
		purpose: "DEVELOPMENT"
	prod: {
		runtime: "kubernetes"
		purpose: "PRODUCTION"

Workstation-specific configuration bits are generated by ns and stored in devhost.textpb. Add this file to .gitignore.


An environment encapsulates configurations used within a particular use-case. By default, Namespace assumes three environments (dev, staging, prod) but you can define an arbitrary number of environments for each workspace.

Infrastructure associated with an environment:

  • Kubernetes cluster (with each environment being deployed to a separate Kubernetes namespace).
  • Environment purpose: DEVELOPMENT, PRODUCTION

By default, ns used the dev environment that is automatically configured by ns prepare local (but you can configure dev for your own cluster with ns prepare existing).

Namespace optimizes builds and deployments per environment (e.g. strips symbols for production).


A module is a source code directory that defines a project root. A single git repository may contain multiple modules. Modules group multiple packages together, all packages within a module have the same version and configuration of dependent modules. As most users only need a single module per workspace, we recommend defining modules alongside the workspace in ns-workspace.cue.

Module name

A module name must correspond to a valid repository path, as ns uses the module name to fetch and manage modules as needed.

module_name: "github.com/username/repo"


Modules can depend on other modules, which allows applications to use servers, resources, etc., from packages that live in a dependency. ns automatically downloads dependencies into a local cache as needed.

Dependencies are managed via ns mod but can also be edited manually in the module definition (ns-workspace.cue).

dependency: {
	"namespacelabs.dev/foundation": {
		version: "f8ad005f1e92ceb88118d2296840703b0ddcbbe8"

When performing changes over multiple repositories in parallel, it's often useful to be able to use the local changes made to a dependent repository without pushing those changes to HEAD. That can be achieved using replace.

replace: {
	"namespacelabs.dev/foundation": "../path/to/local/foundation"


A module can express requirements. For example, it can declare which Namespace CLI versions it is compatible with by requiring a minimum API version.

module: "github.com/username/reponame"
requirements: {
	api: 37

You can determine the API version of your ns installation with ns version. Since namespacelabs.dev/foundation sets requirements and all modules depend on it, setting requirements in your module is usually superfluous.


A package is the unit in Namespace's package management. Packages are defined using standard CUE, over one or more files (with an extension .cue). Packages can declare a set of different types of primitives like servers, tests, etc.

Refer to the syntax reference for more details.

Example of a package that defines a server and an end-to-end test:

server: {
	name: "server"
	// Build the Go code from the package root
	integration: "go"
	// The server exposes port 4000 as a public endpoint
	services: webapi: {
		port: 4000
		kind: "http"
		ingress: true
tests: {
	putAndGet: {
		// Builds the Go binary from the "test" subpackage
		integration: go: pkg: "./test"