A devcontainer is a JSON file that tells Factory exactly how to build your remote workspace—what OS image to start from, which languages to install, which ports to expose, and more.
If you have ever boot-strapped a local machine with brew, apt, or npm install, think of a devcontainer as an automated, shareable version of those steps.

Looking for a deeper dive? See the official GitHub guide “Introduction to dev containers”.

1. Where the File Lives

Create a directory called .devcontainer/ in the root of your repository and add a file named devcontainer.json.

my-repo/
├─ .devcontainer/
│  └─ devcontainer.json
└─ src/

Factory looks for this file automatically when you create a new remote workspace. You can also upload a devcontainer directly in Settings → Workspaces → New Workspace.

2. Minimal JavaScript Example 🌟

The following devcontainer.json sets up Node 20, installs dependencies, and forwards port 3000.

{
  "name": "Node.js Workspace",
  "image": "mcr.microsoft.com/devcontainers/javascript-node:20-bullseye",
  "postCreateCommand": "npm install",
  "forwardPorts": [3000],
  "settings": {
    // VS Code settings that apply _inside_ the remote workspace
    "terminal.integrated.scrollback": 10000
  }
}

Commit this file, create a new remote workspace, and you’re ready to npm run dev without installing Node locally.

3. Other Language Templates

LanguageSnippet
Python{ "image": "mcr.microsoft.com/devcontainers/python:3.12", "postCreateCommand": "pip install -r requirements.txt" }
Go{ "image": "mcr.microsoft.com/devcontainers/go:1.22-bullseye", "postCreateCommand": "go mod download" }
Rust{ "image": "mcr.microsoft.com/devcontainers/rust:1-bullseye", "features": { "llvm": "latest" } }

Feel free to start with these and tweak packages, features, or post-create commands for your stack.

4. Adding Environment Variables & Secrets

Secrets you add in Integrations → Secrets are injected as environment variables at runtime. Reference them in your devcontainer just like you would locally:

{
  "postCreateCommand": "npm config set '//registry.npmjs.org/:_authToken' ${NPM_AUTH_TOKEN}"
}

Need a secret during the image build? Pass it as a build argument:

{
  "build": {
    "dockerfile": "Dockerfile",
    "args": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
  }
}

5. Exposing Ports

Add ports your app listens on to forwardPorts. Factory detects them and shows friendly previews:

{
  "forwardPorts": [3000, 8080]
}

If you spin up multiple services, you can add as many ports as you need.

6. Best Practices

  1. Keep it lean – Start from an official image close to your stack to reduce build time.
  2. Automate everything – Use postCreateCommand to install dev dependencies so teammates (and future you) don’t forget a step.
  3. Pin versions – Lock Node, Python, or package versions to guarantee reproducibility.
  4. Mock external services – Spin up lightweight containers (e.g., SQLite) instead of reaching into staging databases.
  5. Test locally first – The Dev Containers CLI lets you run devcontainer up on your machine before pushing.
  6. Document shortcuts – Add a README_REMOTE_WORKSPACE.md that lists useful commands like npm test or pytest.

7. Troubleshooting Tips

IssueFix
Build takes foreverStart from a lighter base image or cache heavy tools in the image stage.
Command not foundMake sure it’s installed in postCreateCommand or included in the base image.
Port not visibleConfirm the app binds to 0.0.0.0 and that the port is in forwardPorts.
Secret undefinedDouble-check the secret name in Integrations → Secrets matches the variable you reference.

🏁 Next step: Return to Settings → Workspaces and rebuild your remote workspace to apply any devcontainer changes.