I'm coming from a WordPress and PHP background, where I usually kept the entire development stack in one Docker Compose project and deployed it from Git. I'm now learning a more modern JavaScript stack built around Next.js, React, Strapi, PostgreSQL, Puck, Docker Compose, and a Git-based deployment workflow.
My current homelab uses TrueNAS, Docker, a deployment platform, a reverse proxy, and GitHub. I have a basic three-service setup with a frontend, Strapi backend, and PostgreSQL database. It works, but I'm unsure what a sensible development and deployment workflow looks like for a reusable project template.
At the moment, Next.js and Strapi run in development mode inside containers, with the source directories bind-mounted so I can edit them over SSH. The same repository is also used for deployment, which has made me wonder whether development and production should be configured separately.
For real-world Next.js and Strapi projects, do you typically run Node-based tools directly on the development machine while containerizing only supporting services, or do you run the complete development environment in Docker? Do you use separate Compose configurations for development and production? Are Next.js and Strapi kept in one monorepo or separate repositories? Is it normal to use next dev and strapi develop locally, then build immutable images and run next start and strapi start in staging or production? How do you handle local PostgreSQL versus persistent staging databases, Dev Containers, uploads, migrations, and backups?
I'm particularly interested in a simple structure that can be cloned and adapted for different clients. I'd like to hear what people use in practice and what they would change after moving from WordPress/PHP to React, Next.js, and Strapi.
4 Answers
Running the whole stack in Docker is valid, especially when your development machine is remote or you want everyone to use identical Node and package-manager versions. The tradeoff is slower file watching and, in Strapi’s case, sometimes noticeably slower admin rebuilds. If you are comfortable installing Node locally, running Next.js and Strapi on the host while keeping PostgreSQL and other infrastructure in containers can be faster and simpler.
Dev Containers are a good middle ground: the editor connects to a reproducible development container, while you avoid manually entering containers and can standardize the tooling through a configuration file.
For a reusable Strapi template, add bootstrapping and seed logic early. Content types and application code are stored in the project, but roles, permissions, admin configuration, and some initial data live in the database. A fresh client database can otherwise start empty or with an unusable API.
Keep uploads outside the application image, using object storage or a persistent volume. Treat migrations, database backups, secrets, and staging data as explicit deployment concerns rather than things handled implicitly by Compose. Local PostgreSQL can be disposable, while staging and production should use separate persistent databases with their own backup policies.
The biggest Docker-specific trap is node_modules. If the host source directory is mounted over the application directory, it can hide the dependencies installed in the image. That often produces native-module errors because the dependencies were installed for the wrong operating system.
Mount the source directory, but use a separate named or anonymous volume for each container’s node_modules. For Next.js, it is also useful to keep .next in its own volume. PostgreSQL should use a named data volume rather than a source bind mount, and the database should have a health check. Make Strapi wait for the database to become healthy instead of merely waiting for the database container to start.
Keep the three-service Compose project; that level of separation is perfectly reasonable. I would still treat development and production as different targets. Development needs bind mounts, file watchers, polling, and commands such as next dev and strapi develop. Production should build immutable images and run the compiled applications without mounting source code.
A common layout is a shared base Compose file plus a development override, or entirely separate development and production files. Give the frontend and backend separate Dockerfiles, environment templates, health checks, and deployment steps even if they live in the same monorepo. A monorepo is convenient for a reusable template, but it is not mandatory.

That makes sense. I was mainly unsure whether using one Compose project for the frontend, backend, and database was too coarse. I’ll separate the development mounts and commands from the production image configuration, and I’ll make the seed/bootstrap process part of the template rather than relying on manual Strapi setup.