VS Code in the browser — run a full development environment on your FreeBSD server and access it from anywhere.
| Port | 8080 |
| Registry | ghcr.io/daemonless/code-server |
| Source | https://github.com/coder/code-server |
| Website | https://coder.com/docs/code-server |
| Tag | Description | Best For |
|---|---|---|
latest |
Upstream Binary. Built from official release. | Most users — recommended. |
Before deploying, ensure your host environment is ready. See the Quick Start Guide for host setup instructions.
services:
code-server:
image: "ghcr.io/daemonless/code-server:latest"
container_name: code-server
environment:
- PUID=1000 # User ID for the application process
- PGID=1000 # Group ID for the application process
- TZ=UTC # Timezone for the container
- PASSWORD=<PASSWORD> # Password for web UI (leave unset to disable auth)
- DEFAULT_WORKSPACE= # Default folder opened in the editor (default: /config/workspace)
- DISABLE_MDO= # Do not use FreeBSD's mac_do facility to allow executing commands as root from the terminal (optional)
volumes:
- "/path/to/containers/code-server:/config"
ports:
- "8080:8080"
# always (not unless-stopped) so FreeBSD's podman rc.d auto-starts it at boot
restart: alwaysSave as compose.yaml, then run podman-compose up -d.
.env:
# .env
DIRECTOR_PROJECT=code-server
PUID=1000
PGID=1000
TZ=UTC
PASSWORD=<PASSWORD>
DEFAULT_WORKSPACE=
DISABLE_MDO=
appjail-director.yml:
# appjail-director.yml
options:
- virtualnet: ':<random> default'
- nat:
services:
code-server:
name: code_server
options:
- container: 'boot args:--pull'
- expose: '8080:8080 proto:tcp'
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
- PASSWORD: !ENV '${PASSWORD}'
- DEFAULT_WORKSPACE: !ENV '${DEFAULT_WORKSPACE}'
- DISABLE_MDO: !ENV '${DISABLE_MDO}'
volumes:
- code-server: /config
volumes:
code-server:
device: '/path/to/containers/code-server'Makejail:
# Makejail
ARG tag=latest
OPTION overwrite=force
OPTION from=ghcr.io/daemonless/code-server:${tag}
Save the files above, then run appjail-director up.
Note: Exposing ports in AppJail means that your service can be reached from remote hosts. If that is not your intention, do not expose the ports and communicate with the service using the IPv4 address assigned by the virtual network.
podman run -d --name code-server \
-p 8080:8080 \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=UTC \
-e PASSWORD=<PASSWORD> \
-e DEFAULT_WORKSPACE= \
-e DISABLE_MDO= \
-v /path/to/containers/code-server:/config \
ghcr.io/daemonless/code-server:latestSave as run.sh, then run sh run.sh.
appjail oci run -Pd \
-o overwrite=force \
-o container="args:--pull" \
-o virtualnet=":<random> default" \
-o nat \
-o expose="8080:8080 proto:tcp" \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=UTC \
-e PASSWORD=<PASSWORD> \
-e DEFAULT_WORKSPACE= \
-e DISABLE_MDO= \
-o fstab="/path/to/containers/code-server /config <pseudofs>" \
ghcr.io/daemonless/code-server:latest code-serverSave as run.sh, then run sh run.sh.
Note: Exposing ports in AppJail means that your service can be reached from remote hosts. If that is not your intention, do not expose the ports and communicate with the service using the IPv4 address assigned by the virtual network.
Warning
Bastille's OCI support is experimental. It requires buildah, shares the host network stack (inherit), and persists image-declared volumes under --data-path.
services:
code-server:
image: "ghcr.io/daemonless/code-server:latest"
container_name: code-server
network_mode: host # jail shares host networking
environment:
- PUID=1000
- PGID=1000
- TZ=UTC
- PASSWORD=<PASSWORD>
- DEFAULT_WORKSPACE=
- DISABLE_MDO=Save as podman-compose.yml, then run bastille up. Or via CLI:
bastille create -O \
--env PUID=1000 \
--env PGID=1000 \
--env TZ=UTC \
--env PASSWORD=<PASSWORD> \
--env DEFAULT_WORKSPACE= \
--env DISABLE_MDO= \
--data-path /path/to/containers/code-server \
code-server ghcr.io/daemonless/code-server:latest inherit- name: Deploy code-server
containers.podman.podman_container:
name: code-server
image: "ghcr.io/daemonless/code-server:latest"
state: started
restart_policy: always
env:
PUID: "1000"
PGID: "1000"
TZ: "UTC"
PASSWORD: "<PASSWORD>"
DEFAULT_WORKSPACE: ""
DISABLE_MDO: ""
ports:
- "8080:8080"
volumes:
- "/path/to/containers/code-server:/config"Save as code-server-deploy.yaml, then run ansible-playbook code-server-deploy.yaml.
Access at: http://localhost:8080
| Variable | Default | Description |
|---|---|---|
PUID |
1000 |
User ID for the application process |
PGID |
1000 |
Group ID for the application process |
TZ |
UTC |
Timezone for the container |
PASSWORD |
<PASSWORD> |
Password for web UI (leave unset to disable auth) |
DEFAULT_WORKSPACE |
`` | Default folder opened in the editor (default: /config/workspace) |
DISABLE_MDO |
`` | Do not use FreeBSD's mac_do facility to allow executing commands as root from the terminal (optional) |
| Path | Description |
|---|---|
/config |
Configuration directory |
| Port | Protocol | Description |
|---|---|---|
8080 |
TCP | Web UI |
!!! warning "Work in Progress" This image is functional but may change significantly in a future release.
Common dev tools (gcc, clang, llvm, python, gmake, git, ssh) are baked into the image for now.
Podman strips the setuid bit from binaries at runtime, so sudo, su, and doas will not work inside the code-server terminal.
To allow running commands as the root user, we can use FreeBSD's MAC framework and the mdo command which does not depend on the setuid bit being set.
The mac_do kernel module has to be loaded on the host which runs Podman before the container is started.
You can load the module at runtime by running
kldload mac_doTo load the module automatically during boot you can add it to rc.conf with
sysrc kld_list+=mac_doIf the mac_do module is loaded when the container starts, it will automatically install a rule that allows the bsd user to execute commands as root by running mdo <command>.
To disable the installation of the mac_do rule that allows the privilege elevation, you can set the DISABLE_MDO environment variable to true or yes:
services:
code-server:
environment:
- DISABLE_MDO=trueIf the mac_do module is loaded on the host you can install packages in the terminal by running
mdo pkg install...If the module is not loaded or if DISABLE_MDO is set to true you have to install additional packages from the host using podman exec:
# Podman
doas podman exec -it -u root code-server pkg install rust cargo
# AppJail
doas appjail cmd jexec code_server pkg install rust cargo!!! warning "Packages are not persistent" If you recreate the container, you will need to reinstall any packages you have added.
Architectures: amd64
User: bsd (UID/GID via PUID/PGID, defaults to 1000:1000)
Base: FreeBSD 15.1
Need help? Join our Discord community.