play-with-docker/play-with-docker: self-hosting the PWD playground before the hosted service goes dark
You know it, you use it, now it's time to improve it. PWD!.
At a glance
- What is it?
- The open source code behind Play With Docker is an MIT-licensed Go and JavaScript stack that runs Docker-in-Docker instances in the cloud. The hosted service is being retired, so the only remaining use for the repository is running your own.
- Who is it for?
- Adopt this repository only if you need a self-hosted, disposable Docker terminal environment and you accept that the public play-with-docker.com service is being retired, that sessions are capped at 5 playgrounds and deleted after 4 hours, and that the stack must bind ports 80 and 443 for DNS resolution to work. If you want a maintained hosted lab, this is the wrong tool: the README points to Docker Docs for supported labs and guides instead.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 39 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What problem the PWD repository actually solves now
Play With Docker gives a user a free Alpine Linux virtual machine in a browser tab, where they can build and run Docker containers and create Swarm Mode clusters. The README describes the mechanism plainly: under the hood, DIND, or Docker-in-Docker, is used to give the effect of multiple VMs or PCs. That is the whole product. It is a disposable sandbox for people who want to try Docker commands without installing anything locally.
The audience is narrow and specific. It is trainers running workshops, conference speakers who need every attendee to start from the same clean shell, and writers who want a link that opens a working terminal. It is not a production container platform, and nothing in the repository suggests it is meant to be one.
The catch is timing. The README carries a deprecation notice stating that the Play with Docker and Play with Kubernetes systems will be unavailable starting March 1, 2026, and directs readers to Docker Docs for supported labs and guides. So the repository is now a self-hosting kit for a service that is being switched off, not a way to keep using the hosted one. That reframes every question about adoption: you are not choosing PWD over a competitor, you are deciding whether to operate it yourself.
How PWD wires DIND sessions, the l2 router and HAProxy together
The architecture is visible in the repository layout and docker-compose.yml. Three services matter. The pwd container is the daemon; the compose file comments say it always needs to be named this way. It mounts /var/run/docker.sock so it can create networks and launch containers, mounts the source tree at /go/src, and persists sessions to a named volume at /pwd/sessions. The l2 container is the router layer, running from router/l2 and saving its state to /pwd/networks. HAProxy sits in front, publishing port 80 on the host and forwarding to 8080 inside its container, with its configuration mounted from the ./haproxy directory.
The l2 service exposes three host ports: 8022 for SSH, 8053 for DNS, and 443. That DNS listener is why the port question in the README has a blunt answer. PWD needs to run on ports 80 and 443 because DNS resolution depends on it, and the README says no, you cannot change that, while inviting suggestions for improvement. Each playground instance gets a resolvable hostname such as pwd10-0-0-1-8080.host1.localhost, which is how a published port inside a DIND container becomes reachable from a browser.
The top-level directories map onto those roles: api.go and handlers/ for the HTTP surface, provisioner/ and scheduler/ for instance lifecycle, storage/ for session persistence, and router/ for the l2 component. The Go module is declared as github.com/play-with-docker/play-with-docker on go 1.16, with the Docker client pinned to a 2020 pseudo-version. That pinning is the kind of detail that decides whether a rebuild works years later.
Installing PWD locally and opening your first terminal
The README's development section is the install path. The stated requirements are Docker 18.06.0 or later and a stable Go release. Start by cloning the repository and confirming the daemon responds.
git clone https://github.com/play-with-docker/play-with-docker
cd play-with-docker
docker run hello-worldNext, load the IPVS kernel module. The README explains why this is manual: swarms are created inside dind, so the daemon will not load it automatically.
sudo modprobe xt_ipvs
docker swarm init
docker pull franela/dindThe image pull is not optional in spirit. The README warns that standard dind images do not work and only franela ones do. If you want a different version, set the DIND_IMAGE environment variable, for example DIND_IMAGE=franela/docker<version>-rc:dind.
Optionally, with go1.14, you can pre-fetch modules into vendor so the containers need no network access. The README notes the module cache is retained in the pwd and l2 containers, so the download is a one-off if you skip this step. Then start the stack.
go mod vendor
docker-compose upNavigate to http://localhost and click the green Start button to create a session, then ADD NEW INSTANCE to launch a terminal. Two limits apply immediately: a hard-coded maximum of 5 Docker playgrounds per session, and session deletion after 4 hours. For port forwarding during development, the README requires *.localhost to resolve to 127.0.0.1, achieved with a dnsmasq entry of address=/localhost/127.0.0.1 and a change to your machine's default DNS. Copy and paste in the terminal use Ctrl+insert and shift+insert.
The limits you inherit: 5 playgrounds, 4 hours, ports 80 and 443
Three constraints are documented and none of them are configurable through the files shown. The 5-playground cap is described as hard-coded. The 4-hour session lifetime is stated without a setting to extend it. The port binding is answered with a flat no, justified by DNS resolution.
That last one is the real adoption blocker. If the machine you want to run PWD on already serves HTTP or HTTPS for something else, you cannot simply move PWD to another port. You either dedicate the host or put a proxy in front of it, and a proxy in front does not remove the requirement that the DNS path resolves the generated hostnames.
There is a second failure mode in the image dependency. The README is explicit that only franela images work. If franela/dind is not available for the Docker version you need, the playground cannot start, and there is no documented fallback. The repository does not describe a rollback procedure for a running deployment, and the README does not document how to migrate or export sessions before the 4-hour deletion. Treat session state as disposable, because the documentation does.
Alternatives: Docker Desktop, hosted labs, or your own VM
The honest alternative depends on why you wanted PWD. If the goal is a local Docker environment, Docker Desktop or a plain Docker Engine install on Linux gives you the same CLI without a browser terminal, and it does not require xt_ipvs, swarm mode, or wildcard DNS. The difference in approach is that PWD isolates each user inside a DIND container on a shared host, while a local install gives one person one daemon. For a single developer, the local install is simpler and has no 4-hour timer.
If the goal is a classroom, the README's deprecation notice points to Docker Docs for supported labs and guides. That is the project's own stated replacement path, and it is a hosted option rather than something you operate. The trade-off is control: you get someone else's maintenance schedule and content, but you do not run HAProxy, l2, and a Docker socket mount on your own host.
If you need multi-user disposable terminals and want to stay self-hosted, the closest thing in this repository is the repository itself, deployed with docker-compose up. There is no lighter-weight mode documented. The scheduler and provisioner components exist because the hosted service needed to place many sessions across hosts; a single-host deployment still carries that machinery.
Maintenance, licensing and what a rebuild costs
The repository is not archived, and the last push was on 2026-08-24. That is recent enough that the code is still receiving changes, but the README's own deprecation notice is the more important signal about direction: the hosted systems are being retired, so upstream effort is unlikely to target new features for the public service.
The licence is MIT, which permits commercial and private use, modification, and redistribution provided the copyright notice and permission notice are included. That is a permissive position and it means self-hosting carries no licence fee. It does not settle the franela/dind image, which is a separate artifact on Docker Hub with its own terms; the repository does not state them, so check the image page before you build a product on it.
The upgrade cost is dominated by the Go toolchain pin. The Dockerfile builds with golang:1.16 and CGO_ENABLED=0, and go.mod declares go 1.16 with a Docker client pinned to a 2020 pseudo-version. Moving to a current Go release means updating that dependency tree, and the module list includes several indirect pins from the same era. Budget for a dependency pass, not a version bump. The Dockerfile itself is a two-stage build that produces an Alpine image exposing port 3000 internally, so the container port and the host port are different numbers and worth keeping straight when you debug.
Editorial conclusion
Adopt this repository only if you need a self-hosted, disposable Docker terminal environment and you accept that the public play-with-docker.com service is being retired, that sessions are capped at 5 playgrounds and deleted after 4 hours, and that the stack must bind ports 80 and 443 for DNS resolution to work. If you want a maintained hosted lab, this is the wrong tool: the README points to Docker Docs for supported labs and guides instead. Before committing, verify that the franela/dind image you intend to use still exists on Docker Hub, that your host can load the xt_ipvs kernel module, and that your DNS plan for wildcard *.localhost resolution is in place.
Frequently asked questions
Is Play With Docker free?
The repository is MIT licensed, so self-hosting costs nothing in licence fees. The hosted service at play-with-docker.com is being retired, with the README stating the systems will be unavailable starting March 1, 2026.
What happened to Play With Docker?
The README carries a deprecation notice saying the Play with Docker and Play with Kubernetes systems will be unavailable starting March 1, 2026, and points readers to Docker Docs for supported labs and guides. The source code remains available to run yourself.
How do I use Play With Docker?
On a self-hosted instance, you navigate to http://localhost, click the green Start button to create a session, then click ADD NEW INSTANCE to launch a terminal. Copy and paste inside that terminal use Ctrl+insert and shift+insert.
What is Play With Docker?
It gives you the experience of a free Alpine Linux virtual machine in the cloud where you can build and run Docker containers and create clusters with Docker features like Swarm Mode. Under the hood it uses Docker-in-Docker to simulate multiple VMs.
Is there an online alternative to Play With Docker?
The README's deprecation notice directs users to Docker Docs for supported labs and guides. The repository does not name any other playground service.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/play-with-docker-play-with-docker)