{"id":23219640,"url":"https://github.com/barcek/docnxgres","last_synced_at":"2026-04-10T13:32:22.919Z","repository":{"id":157419473,"uuid":"332856802","full_name":"barcek/docNxgres","owner":"barcek","description":"containerize a web app | four-container stack | front- \u0026 backend | working template | Docker Compose, Nginx, Node w/ Express.js, Redis \u0026 PostgreSQL","archived":false,"fork":false,"pushed_at":"2021-07-23T20:36:02.000Z","size":632,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-30T11:23:12.715Z","etag":null,"topics":["backend","docker","nginx","nodejs","redis","web-development"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/barcek.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-25T19:17:48.000Z","updated_at":"2024-08-15T11:23:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"c14b4cb5-dab2-4887-ac9b-5ffd898d5ce6","html_url":"https://github.com/barcek/docNxgres","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/barcek/docNxgres","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/barcek%2FdocNxgres","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/barcek%2FdocNxgres/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/barcek%2FdocNxgres/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/barcek%2FdocNxgres/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/barcek","download_url":"https://codeload.github.com/barcek/docNxgres/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/barcek%2FdocNxgres/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31645284,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-10T07:40:12.752Z","status":"ssl_error","status_checked_at":"2026-04-10T07:40:11.664Z","response_time":98,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backend","docker","nginx","nodejs","redis","web-development"],"created_at":"2024-12-18T21:36:14.198Z","updated_at":"2026-04-10T13:32:22.898Z","avatar_url":"https://github.com/barcek.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# docNxgres\n\nA four-container back end using Docker Compose, with one container each for an Nginx reverse proxy server, an Express.js application server, a Redis cache and a PostgreSQL database.\n\nThe setup serves a simple static front end demonstrating the flow of data from the client through the reverse proxy and application server to the database. The whole is intended as a working template for similar setups and a sandbox for learning and experimentation with more advanced features and interactions.\n\nIt is not fully production ready, absent for example a production ready session store. See [Notes on the services](#notes-on-the-services) below for more information.\n\nIn certain areas it provides as options one or more additional lines commented out, and includes comments on choices available and those made for this version.\n\nFor the whole at a glance, see [the current repository tree](#repository-tree).\n\n- [Getting started](#getting-started)\n    - [Pulling the images](#pulling-the-images)\n    - [Listing all elements](#listing-all-elements)\n    - [Removal in full or part](#removal-in-full-or-part)\n- [Unit \u0026 integration tests](#unit--integration-tests)\n- [Environment \u0026 mode](#environment--mode)\n    - [Environment variables](#environment-variables)\n    - [Development \u0026 production](#development--production)\n        - [Combined](#combined)\n        - [Separate](#separate)\n    - [Running the application server alone](#running-the-application-server-alone)\n- [Notes on the services](#notes-on-the-services)\n    - [app-proxy (Nginx reverse proxy server)](#app-proxy-nginx-reverse-proxy-server)\n    - [app-server (Express.js application server)](#app-server-expressjs-application-server)\n    - [app-cache (Redis cache)](#app-cache-redis-cache)\n    - [app-db (PostgreSQL database)](#app-db-postgresql-database)\n- [Development plan](#development-plan)\n- [Repository tree](#repository-tree)\n\n## Getting started\n\nYou'll need both Docker and Docker Compose installed. Your Docker install may have included Compose. You can check which are present by running the commands `docker -v` and `docker-compose -v` to get the version numbers.\n\nTo create and run the containers, clone this repository to a new directory and at the root of that directory run the following:\n\n```shell\ndocker-compose up\n```\n\nTo stop the containers, `Ctrl-C` can be used.\n\nTo remove the containers, run the following:\n\n```shell\ndocker-compose down\n```\n\nAlternatively, or if this fails, the following command runs a script to remove each container by name:\n\n```shell\ndocker rm app-server app-cache app-db app-proxy\n```\n\n### Pulling the images\n\nIn the event that one or more of the four Docker images used is not pulled automatically from Docker Hub, the appropriate `docker pull` command can be used. For the specific images used:\n\n```shell\ndocker pull node:16.3.0-alpine3.13\ndocker pull redis:6.2.4-alpine3.13\ndocker pull postgres:13.3-alpine\ndocker pull nginx:1.21.0-alpine\n```\n\n### Listing all elements\n\nTo list the current images, containers and volumes, run the following command:\n\n```shell\ndocker ps -a \u0026\u0026 docker images \u0026\u0026 docker volume ls\n```\n\nThis is also available as a script, run with the following command:\n\n```shell\nnpm run compose:ls\n```\n\n### Removal in full or part\n\nAssuming that the new directory name is 'docNxgres', and that the Express.js service name in 'docker-compose.yml' is the default `server`, it should be possible to remove the application server image by running the following:\n\n```shell\ndocker rmi docnxgres_server\n```\n\nAssuming 'docNxgres' as above, and that the PostgreSQL service name is the default `db`, it should be possible to remove the data volumes by running the following:\n\n```shell\ndocker volume rm docnxgres_app-cache-data docnxgres_app-db-data\n```\n\nThe command `npm run compose:rm:c` runs a script to remove the containers, specifically `docker-compose down` (see above). The commands `npm run compose:rm:ci`, `npm run compose:rm:cv` and `npm run compose:rm:civ` each run a script combining two or three removals, removing either the containers and the image (`:ci`), the containers and the database volume (`:cv`) or the containers, the image and the volume (`:civ`). These scripts are also uses of `docker-compose down`.\n\nIf permissions for Docker are not yet set up, it should be possible to precede each of the commands above with `sudo`.\n\n## Unit \u0026 integration tests\n\nThe tests use the npm packages `mocha`, `chai` and `chai-http` as dev dependencies. They assume that the test database container defined in 'docker-compose_test.yml' is running.\n\nThe database container can be run with the following command:\n\n```shell\nnpm run test:up\n```\n\nThe tests can then be run using:\n\n```shell\nnpm test\n```\n\nThis script sets four environment variables to override development values defined in the '.env' file and contains the `mocha --recursive` command. The `--recursive` flag ensures tests in subdirectories are also run.\n\nThere is also a 'watch' script to watch for and test on changes:\n\n```shell\nnpm run watch\n```\n\nWhen complete, the containers can be stopped with `Ctrl-C` and the containers and volumes removed using the command:\n\n```shell\nnpm run test:down\n```\n\nAll three scripts are among those defined in the 'package.json' file.\n\n## Environment \u0026 mode\n\n### Environment variables\n\nEnvironment variables are set in five files.\n\nThe root directory contains a '.env' file with environment variables. For the database, there are two sets of variables, one for development mode and one for production mode. The majority of the variables contain placeholder values. For use in production, the default production password value should be changed.\n\nAlso in the root directory are two Dockerfiles, one for development and one for production. Each sets the environment variable 'NODE_ENV' to the corresponding value.\n\nFinally, the root directory contains four 'docker-compose' files:\n\n1. 'docker-compose.yml', which has settings for the dev and prod variants with the dev variant commented out;\n2. 'docker-compose_dev.yml' for the dev variant;\n3. 'docker-compose_prod.yml' for the prod variant;\n4. 'docker-compose_test.yml' for the test containers.\n\nEach of the first three of these does the following:\n\n- sets the `SERVER_PORT` environment variable;\n- uses three of the database variables set in '.env' to initialize the database container, whether for development or production, with the alternate three commented out (see [Development \u0026 production](#development--production) below);\n- passes the `LOG_FORMAT` environment variable to the reverse proxy server container.\n\nThe application server file 'src/config/index.js' accesses the '.env' file using the `dotenv` package. The applicable set of database variables, whether for development or production, is selected using the value of the `NODE_ENV` environment variable. `NODE_ENV` is also used to set an `IN_PROD` environment variable. All relevant variables are then exported for use elsewhere in the application server code.\n\n### Development \u0026 production\n\n#### Combined\n\nThe file 'docker-compose.yml' specifies whether the application server image is to be built for development or for production.\n\nThe default mode is production mode, but this is not to imply that the setup is fully production ready. See [Notes on the services](#notes-on-the-services) below for more information.\n\nFor development mode, uncomment line 10 - `dockerfile: Dockerfile_dev` - and comment out line 11 - `dockerfile: Dockerfile_prod`.\n\nFor development, it is also possible to uncomment line 17 - `./:/usr/src/server/` - to allow changes in the source code on the host system to be applied within the container. This allows `nodemon` to be restarted by making a file change, which may be required if the application server container is ready before the database.\n\nIf different database settings are required for development, these can be set in the corresponding environment variables in the '.env' file. Those variables can then be uncommented in the file 'docker-compose.yml' and the alternate variables for production commented out.\n\n#### Separate\n\nThe files 'docker-compose_dev.yml' and 'docker-compose_prod.yml' each contain the settings for the corresponding variant of the file 'docker-compose.yml', avoiding the need to comment out and uncomment lines.\n\nThe use of 'prod' for one of the variants is not to imply that the setup is fully production ready. See [Notes on the services](#notes-on-the-services) below for more information.\n\nTo run the containers with a variant file, the `-f` flag can be used, as below:\n\n```shell\ndocker-compose -f docker-compose_dev.yml up\n```\n\nA script containing the relevant command is available for each variant file, and both can be found in the file 'package.json'. The script for the dev variant is run with the following command:\n\n```shell\nnpm run compose:dev\n```\n\nTo remove the containers, the standard command can be used:\n\n```shell\ndocker-compose down\n```\n\nAgain, this is the content of the following script:\n\n```shell\nnpm run compose:rm:c\n```\n\nThe commands `npm run compose:rm:ci`, `npm run compose:rm:cv` and `npm run compose:rm:civ` can be used to go further, each running a script combining two or three removals, removing either the containers and the image (`:ci`), the containers and the database volume (`:cv`) or the containers, the image and the volume (`:civ`).\n\n### Running the application server alone\n\nTo run the application server outside of the four-container setup, the value of each preferred -`_HOST` environment variable should be changed from the service name `cache` or `db` to an alternative, presumably `localhost`.\n\nTwo start scripts are available in the file 'package.json'.\n\nTo use `nodemon`, run:\n\n```shell\nnpm run dev\n```\n\nOtherwise, run:\n\n```shell\nnpm run prod\n```\n\n## Notes on the services\n\n### app-proxy (Nginx reverse proxy server)\n\nThe 'nginx.conf' and 'default.conf.template' configuration files for the Nginx reverse proxy are mounted into the container. Changes made outside of the container can be applied within by restarting the containers.\n\n- At the top of 'nginx.conf', `user` is set to `nobody`, but an alternative user may be preferred.\n- Around midway down 'nginx.conf' and in 'default.conf.template', logging is set to a light level. Specifially, in 'nginx.conf', a custom `discreet` log format is defined, while in 'default.conf.template', the log file 'proxy_access.log' has its format set via the `LOG_FORMAT` environment variable to this `discreet` format. In 'nginx.conf', the line for error logging into 'proxy_error.log' has been commented out as an equivalent discreet format cannot trivially be applied. The intention here is to avoid data collection issues by default, but if greater collection is required, the log format can be modified or one or more new formats added, the error logging line uncommented and moved to 'default.conf.template' and/or a bind mount for 'proxy_error.log' added to each relevant 'docker-compose' file as for the access log.\n- In the server block in 'default.conf.template', the reverse proxy is set to listen on port 80. In each 'docker-compose' file, port 80 is mapped to port 8080 to avoid conflict, but this may need to be changed.\n\nReading up on [the Nginx image](https://hub.docker.com/_/nginx) is recommended.\n\n### app-server (Express.js application server)\n\nAs described in [Environment variables](#environment-variables) above, the file 'src/config/index.js' accesses the '.env' file using the `dotenv` package and exports all relevant environment variables for use elsewhere in the application server code.\n\nFor development, it is possible to allow changes in the source code on the host system to be applied within the container (see [Development \u0026 production](#development--production) above).\n\n- The file 'src/index.js' is the entrypoint for the app, using the `cluster` module to start additional processes based on CPUs and the `SERVER_MULTIPLIER` environment variable.\n- The file 'src/app.js' contains several comments explaining choices available and made for this version.\n    1. The `csurf` package providing protection against CSRF requires the use of the `cookie-parser` or the `express-session` package. In this case, `cookie-parser` has been chosen, for greater simplicity and in line with the approach to data collection issues taken also with logging (see below).\n    However, 'src/app.js' contains lines also for `express-session`, specifically lines requiring the package, importing the `SSN` environment variables for session configuration, for the configuration itself and for the `csurf` middleware. These lines are commented out, available as an alternative, albeit with additional changes needed. Using npm, `express-session` can be installed with the command `npm install express-session` and `cookie-parser` uninstalled with `npm uninstall cookie-parser`.\n    The configuration for both middlewares assumes the use of HTTPS in production. If `express-session` were to be used in production, the Nginx reverse proxy server would require directives for `X-Forwarded` headers and an alternative session store would need to be used in place of the non-production MemoryStore. While MemoryStore does allow the application server to run if the `cluster` module is not used, e.g. if `app.listen` is applied in 'app.js' and 'index.js' omitted, a warning is given.\n    2. With the default four-container setup, static files are served from the Nginx reverse proxy server. However, 'src/app.js' does contain a line for static serving via Express, by means of the `express.static` middleware. This line is commented out, available as an alternative when the Nginx container is not in use.\n    3. The file assumes that data posted from client is sent as a JSON string. However, both 'src/app.js' and 'src/public/script.js' contain lines for use of URI encoding. These lines are commented out, available as an alternative.\n- The file 'src/app.js' also requires the `logger` middleware from the 'log' folder, using the `morgan` package. As with the reverse proxy server, logging is set to a light level (see [app-proxy (Nginx reverse proxy server)](#app-proxy-nginx-reverse-proxy-server) above). The intention here is to avoid data collection issues by default, but if greater collection is required, the log format can be modified, one of the `morgan` presets listed in 'src/log/logger.js' used or one or more new formats added. The 'log' folder also contains the file 'utils.js' offering an `addLogEntry` function for use in error logging.\n\nReading up on [the Node.js image](https://hub.docker.com/_/node) is recommended.\n\nThe `npm audit` command can be used to run a security audit on the dependencies used, with the process returning information on updates where available. The command `npm audit fix` can be used instead or thereafter to install compatible updates. See the npm documentation for [more detail](https://docs.npmjs.com/auditing-package-dependencies-for-security-vulnerabilities).\n\n### app-cache (Redis cache)\n\n- In each 'docker-compose' file, the 'cache' service is assigned a volume named 'app-cache-data' in which data is persisted between uses of the container. If no longer needed, this volume can be removed (see [Getting started](#getting-started) above).\n\nReading up on [the Redis image](https://hub.docker.com/_/redis) is recommended.\n\n### app-db (PostgreSQL database)\n\nAs described in [Environment variables](#environment-variables) above, three of the database variables set in '.env' are used in each 'docker-compose' file to initialize the database container.\n\nThere are two sets of variables, one for development mode and one for production mode. The majority of the variables contain placeholder values. For use in production, the default production password value should be changed.\n\nThe environment variables `POSTGRES_USER` and `POSTGRES_PASSWORD` are required to set up a superuser for the container, while `POSTGRES_DB` is optional, used to provide a different name for the default database created when the container is run.\n\n- If not already present in the database, an 'entries' table is created in the database by code in the file 'src/db/entries.js' in the application server container.\n- In each 'docker-compose' file, the 'db' service is assigned a volume named 'app-db-data' in which data is persisted between uses of the container. If no longer needed, this volume can be removed (see [Getting started](#getting-started) above).\n\nReading up on [the PostgreSQL image](https://hub.docker.com/_/postgres) is recommended.\n\n## Development plan\n\nThe following are possible next steps in the development of the code base. The general medium-term aim is a more fully-featured, production-ready template which remains useful as an aid to learning. Pull requests are welcome for these and any other potential improvements.\n\n- implement a production-grade session store\n- extend the demonstration REST API to include further CRUD operations and more complex queries\n- provide a parallel GraphQL implementation\n- include error logging in the application server log stream\n- extend the set of unit \u0026 integration tests\n- add rate limiting to the reverse proxy server\n- add file caching to the reverse proxy server\n- migrate the project to TypeScript, retaining optional use of JavaScript only for ease of access\n\n## Repository tree\n\n```\n./\n├─ logs\n│  ├─ proxy_access.log\n│  └─ server.log\n├─ src\n│  ├─ cache\n│  │  ├─ client.js\n│  │  ├─ index.js\n│  │  └─ operations.js\n│  ├─ config\n│  │  └─ index.js\n│  ├─ controllers\n│  │  ├─ entries.js\n│  │  └─ index.js\n│  ├─ db\n│  │  ├─ crud.js\n│  │  ├─ entries.js\n│  │  ├─ index.js\n│  │  ├─ pool.js\n│  │  └─ table.js\n│  ├─ log\n│  │  ├─ index.js\n│  │  ├─ logger.js\n│  │  ├─ logstream.js\n│  │  └─ utils.js\n│  ├─ public\n│  │  ├─ script.js\n│  │  └─ style.css\n│  ├─ routes\n│  │  ├─ entries.js\n│  │  ├─ error.js\n│  │  └─ index.js\n│  ├─ services\n│  │  ├─ entries.js\n│  │  └─ index.js\n│  ├─ utils\n│  │  ├─ format.js\n│  │  └─ index.js\n│  ├─ views\n│  │  ├─ includes\n│  │  │  ├─ footer.pug\n│  │  │  ├─ form.pug\n│  │  │  └─ header.pug\n│  │  ├─ error.pug\n│  │  ├─ index.pug\n│  │  └─ layout.pug\n│  ├─ app.js\n│  └─ index.js\n├─ test\n│  ├─ db\n│  │  ├─ crud.test.js\n│  │  ├─ db.test.js\n│  │  └─ table.test.js\n│  ├─ log\n│  │  ├─ log.test.js\n│  │  ├─ logger.test.js\n│  │  ├─ logstream.test.js\n│  │  └─ utils.test.js\n│  ├─ utils\n│  │  ├─ format.test.js\n│  │  └─ utils.test.js\n│  └─ app.test.js\n├─ .dockerignore\n├─ .env\n├─ .gitignore\n├─ Dockerfile_dev\n├─ Dockerfile_prod\n├─ LICENSE.txt\n├─ README.md\n├─ default.conf.template\n├─ docker-compose.yml\n├─ docker-compose_dev.yml\n├─ docker-compose_prod.yml\n├─ docker-compose_test.yml\n├─ nginx.conf\n├─ package.json\n└─ package-lock.json\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbarcek%2Fdocnxgres","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbarcek%2Fdocnxgres","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbarcek%2Fdocnxgres/lists"}