├── .env ├── .github ├── issue_template.md └── workflows │ └── workflow.yml ├── .gitignore ├── Dockerfile ├── LICENSE.md ├── Makefile ├── README.md ├── compose.yml ├── docker.mk ├── tests ├── .env ├── Dockerfile ├── compose.yml ├── requirements.txt ├── run.sh ├── settings-additions.py └── tests.sh └── traefik.yml /.env: -------------------------------------------------------------------------------- 1 | ### Documentation available at https://wodby.com/docs/stacks/python/local 2 | ### Changelog can be found at https://github.com/wodby/docker4python/releases 3 | ### Images tags format explained at https://github.com/wodby/docker4python#images-tags 4 | 5 | ### PROJECT SETTINGS 6 | 7 | PROJECT_NAME=my_python_project 8 | PROJECT_BASE_URL=python.docker.localhost 9 | 10 | DB_NAME=python 11 | DB_USER=python 12 | DB_PASSWORD=python 13 | DB_HOST=postgres 14 | DB_ADAPTER=postgresql 15 | DB_ENGINE=django.db.backends.postgresql 16 | #DB_ENGINE=django.db.backends.mysql 17 | 18 | ### --- PYTHON ---- 19 | 20 | # Linux (uid 1000 gid 1000) 21 | 22 | PYTHON_TAG=3.13-dev-0.25.0 23 | #PYTHON_TAG=3.12-dev-0.25.0 24 | #PYTHON_TAG=3.11-dev-0.25.0 25 | #PYTHON_TAG=3.10-dev-0.25.0 26 | #PYTHON_TAG=3.9-dev-0.25.0 27 | 28 | # macOS (uid 501 gid 20) 29 | 30 | #PYTHON_TAG=3.13-dev-macos-0.25.0 31 | #PYTHON_TAG=3.12-dev-macos-0.25.0 32 | #PYTHON_TAG=3.11-dev-macos-0.25.0 33 | #PYTHON_TAG=3.10-dev-macos-0.25.0 34 | #PYTHON_TAG=3.9-dev-macos-0.25.0 35 | 36 | ### --- NGINX ---- 37 | 38 | NGINX_TAG=1.28-5.43.0 39 | #NGINX_TAG=1.27-5.43.0 40 | 41 | ### --- NODE --- 42 | 43 | NODE_TAG=22-dev-1.51.0 44 | #NODE_TAG=20-dev-1.51.0 45 | #NODE_TAG=18-dev-1.51.0 46 | 47 | ### --- POSTGRESQL ---- 48 | 49 | POSTGRES_TAG=17-1.35.6 50 | #POSTGRES_TAG=16-1.35.6 51 | #POSTGRES_TAG=15-1.35.6 52 | #POSTGRES_TAG=14-1.35.6 53 | #POSTGRES_TAG=13-1.35.6 54 | 55 | ### --- MARIADB ---- 56 | 57 | #DB_ROOT_PASSWORD=password 58 | 59 | MARIADB_TAG=11.4-3.32.0 60 | #MARIADB_TAG=10.11-3.32.0 61 | #MARIADB_TAG=10.6-3.32.0 62 | #MARIADB_TAG=10.5-3.32.0 63 | 64 | ### --- VALKEY --- 65 | 66 | VALKEY_TAG=8-1.0.1 67 | #VALKEY_TAG=7-1.0.1 68 | 69 | ### --- VARNISH --- 70 | 71 | VARNISH_TAG=6.0-4.21.0 72 | 73 | ### --- OPENSEARCH --- 74 | 75 | OPENSEARCH_TAG=2 76 | OPENSEARCH_ADMIN_PASSWORD=VERY%@bad123password 77 | 78 | ### --- SOLR --- 79 | 80 | SOLR_TAG=9-5.1.6 81 | 82 | ### OTHERS 83 | 84 | ADMINER_TAG=5-4.2.4 85 | MEMCACHED_TAG=1-2.19.1 86 | RSYSLOG_TAG=latest 87 | OPENSMTPD_TAG=7-1.26.0 88 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | ### Codebase 2 | 3 | ### Host OS 4 | 5 | e.g. macOS Sierra 6 | 7 | ### Docker info output 8 | ``` 9 | # Run "docker info" on the host machine and paste output here 10 | ``` 11 | 12 | ### Docker compose file 13 | 14 | Make sure you remove all commented services. 15 | 16 | ```yml 17 | # Content of your compose.yml file. Make sure you remove all sensible information you might have there. 18 | ``` 19 | 20 | ### Logs output 21 | ``` 22 | # Run "docker compose logs [service]". Let's say you get 500 error for some reason then it'll be helpful to provide logs for python and nginx services. 23 | ``` 24 | -------------------------------------------------------------------------------- /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | tags: 9 | - '*' 10 | 11 | pull_request: 12 | 13 | jobs: 14 | test: 15 | strategy: 16 | matrix: 17 | version: [ '3.13', '3.12', '3.11','3.10','3.9' ] 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: test 22 | env: 23 | PYTHON_VER: ${{ matrix.version }} 24 | run: make test 25 | release: 26 | runs-on: ubuntu-latest 27 | if: startsWith(github.ref, 'refs/tags/') 28 | needs: test 29 | steps: 30 | - uses: actions/checkout@v2 31 | - name: prepare artifact 32 | run: cp docker.mk Makefile && tar -czf docker4python.tar.gz compose.yml Makefile .env traefik.yml 33 | - name: get tag name 34 | id: vars 35 | run: echo ::set-output name=tag::${GITHUB_REF#refs/*/} 36 | - uses: xresloader/upload-to-github-release@v1 37 | id: upload_artifact 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | with: 41 | file: docker4python.tar.gz 42 | overwrite: true 43 | tags: true 44 | draft: false 45 | tag_name: ${{ steps.vars.outputs.tag }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | mutagen.yml.lock -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PYTHON_TAG 2 | 3 | FROM wodby/python:${PYTHON_TAG} 4 | 5 | RUN pip install --upgrade pip 6 | #RUN pip install pipenv 7 | 8 | COPY requirements.txt ./ 9 | 10 | #COPY Pipfile Pipfile.lock ./ 11 | 12 | # Install additional dev packages for native extensions (only for -dev python tag) 13 | #RUN sudo apk add --update missing-package 14 | 15 | RUN pip install -r requirements.txt 16 | #RUN pipenv install --skip-lock --system --dev 17 | 18 | # We assume your project name is "myapp". 19 | ENV GUNICORN_APP myapp.wsgi:application 20 | # By default we start Gunicorn server, modify to change it. 21 | # If your project is in a subdirectory. 22 | #ENV GUNICORN_PYTHONPATH subdir 23 | CMD ["/etc/init.d/gunicorn"] 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Wodby, Inc. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include docker.mk 2 | 3 | .PHONY: test 4 | 5 | PYTHON_VER ?= "3.13" 6 | 7 | test: 8 | cd ./tests && PYTHON_VER=$(PYTHON_VER) ./run.sh 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker-based Python stack 2 | 3 | [![Build Status](https://github.com/wodby/docker4python/workflows/Run%20tests/badge.svg)](https://github.com/wodby/docker4python/actions) 4 | 5 | ## Introduction 6 | 7 | Docker4Python is a set of docker images optimized for Python applications (suitable for Django). Use 8 | `compose.yml` file from the [latest stable release](https://github.com/wodby/docker4python/releases) to spin up local environment on Linux, Mac OS X and Windows. 9 | 10 | - Read the docs on [**how to use**](https://wodby.com/docs/stacks/python/local#usage) 11 | - Ask questions on [Discord](http://discord.wodby.com/) 12 | - Ask questions on [Slack](http://slack.wodby.com/) 13 | - Follow [@wodbycloud](https://twitter.com/wodbycloud) for updates announcements 14 | 15 | ## Stack 16 | 17 | The Python stack consist of the following containers: 18 | 19 | | Container | Versions | Image | ARM64 support | Enabled by default | 20 | |-----------------------|-----------------------------|-------------------------------------------|---------------|--------------------| 21 | | [Nginx] | 1.28, 1.27 | [wodby/nginx] | ✓ | ✓ | 22 | | [Python] | 3.13, 3.12, 3.11, 3.10, 3.9 | [wodby/python] | ✓ | ✓ | 23 | | [PostgreSQL] | 17, 16, 15, 14, 13 | [wodby/postgres] | ✓ | ✓ | 24 | | [Valkey] | 8, 7 | [wodby/valkey] | ✓ | ✓ | 25 | | [MariaDB] | 11.4, 10.6, 10.5, 10.4 | [wodby/mariadb] | ✓ | | 26 | | [Node.js] | 22, 20, 18 | [wodby/node] | ✓ | | 27 | | [Varnish] | 6.0 | [wodby/varnish] | ✓ | | 28 | | [Solr] | 9 | [wodby/solr] | ✓ | | 29 | | OpenSearch | 2 | [opensearchproject/opensearch] | ✓ | | 30 | | OpenSearch Dashboards | 2 | [opensearchproject/opensearch-dashboards] | ✓ | | 31 | | [Memcached] | 1 | [wodby/memcached] | ✓ | | 32 | | [Rsyslog] | latest | [wodby/rsyslog] | ✓ | | 33 | | Gotenberg | latest | [gotenberg/gotenberg] | ✓ | ✓ | 34 | | Mailpit | latest | [axllent/mailpit] | ✓ | ✓ | 35 | | [OpenSMTPD] | 7 | [wodby/opensmtpd] | ✓ | | 36 | | Adminer | 5 | [wodby/adminer] | ✓ | | 37 | | Traefik | latest | [_/traefik] | ✓ | ✓ | 38 | 39 | ## Documentation 40 | 41 | Full documentation is available at https://wodby.com/docs/stacks/python/local 42 | 43 | ## Images' tags 44 | 45 | Images tags format is `[VERSION]-[STABILITY_TAG]` where: 46 | 47 | `[VERSION]` is the _version of an application_ (without patch version) running in a container, e.g. 48 | `wodby/nginx:1.15-x.x.x` where Nginx version is `1.15` and 49 | `x.x.x` is a stability tag. For some images we include both major and minor version like Python 50 | `2.5`, for others we include only major like Valkey `7`. 51 | 52 | `[STABILITY_TAG]` is the _version of an image_ that corresponds to a git tag of the image repository, e.g. 53 | `wodby/mariadb:10.2-3.3.8` has MariaDB `10.2` and stability tag [ 54 | `3.3.8`](https://github.com/wodby/mariadb/releases/tag/3.3.8). New stability tags include patch updates for applications and image's fixes/improvements (new env vars, orchestration actions fixes, etc). Stability tag changes described in the corresponding a git tag description. Stability tags follow [semantic versioning](https://semver.org/). 55 | 56 | We highly encourage to use images only with stability tags. 57 | 58 | ## Maintenance 59 | 60 | We regularly update images used in this stack and release them together, see [releases page](https://github.com/wodby/docker4python/releases) for full changelog and update instructions. Most of routine updates for images and this project performed by [the bot](https://github.com/wodbot) via scripts located at [wodby/images](https://github.com/wodby/images). 61 | 62 | ## Other Docker4x projects 63 | 64 | * [docker4php](https://github.com/wodby/docker4php) 65 | * [docker4drupal](https://github.com/wodby/docker4drupal) 66 | * [docker4wordpress](https://github.com/wodby/docker4wordpress) 67 | * [docker4ruby](https://github.com/wodby/docker4ruby) 68 | 69 | ## License 70 | 71 | This project is licensed under the MIT open source license. 72 | 73 | [MariaDB]: https://wodby.com/docs/stacks/python/containers#mariadb 74 | 75 | [Memcached]: https://wodby.com/docs/stacks/python/containers#memcached 76 | 77 | [Nginx]: https://wodby.com/docs/stacks/python/containers#nginx 78 | 79 | [Node.js]: https://wodby.com/docs/stacks/python/containers#node 80 | 81 | [OpenSMTPD]: https://wodby.com/docs/stacks/python/containers#opensmtpd 82 | 83 | [PostgreSQL]: https://wodby.com/docs/stacks/python/containers#postgres 84 | 85 | [Valkey]: https://wodby.com/docs/stacks/python/containers#valkey 86 | 87 | [Rsyslog]: https://wodby.com/docs/stacks/python/containers#rsyslog 88 | 89 | [Python]: https://wodby.com/docs/stacks/python/containers#python 90 | 91 | [Solr]: https://wodby.com/docs/stacks/solr 92 | 93 | [Varnish]: https://wodby.com/docs/stacks/python/containers#varnish 94 | 95 | [_/traefik]: https://hub.docker.com/_/traefik 96 | 97 | [gotenberg/gotenberg]: https://hub.docker.com/r/gotenberg/gotenberg 98 | 99 | [blackfire/blackfire]: https://hub.docker.com/r/blackfire/blackfire 100 | 101 | [axllent/mailpit]: https://hub.docker.com/r/axllent/mailpit 102 | 103 | [wodby/adminer]: https://hub.docker.com/r/wodby/adminer 104 | 105 | [wodby/mariadb]: https://github.com/wodby/mariadb 106 | 107 | [wodby/memcached]: https://github.com/wodby/memcached 108 | 109 | [wodby/nginx]: https://github.com/wodby/nginx 110 | 111 | [wodby/node]: https://github.com/wodby/node 112 | 113 | [wodby/opensmtpd]: https://github.com/wodby/opensmtpd 114 | 115 | [wodby/postgres]: https://github.com/wodby/postgres 116 | 117 | [wodby/valkey]: https://github.com/wodby/valkey 118 | 119 | [wodby/rsyslog]: https://hub.docker.com/r/wodby/rsyslog 120 | 121 | [wodby/python]: https://github.com/wodby/python 122 | 123 | [wodby/solr]: https://github.com/wodby/solr 124 | 125 | [wodby/varnish]: https://github.com/wodby/varnish 126 | 127 | [opensearchproject/opensearch]: https://hub.docker.com/r/opensearchproject/opensearch 128 | 129 | [opensearchproject/opensearch-dashboards]: https://hub.docker.com/r/opensearchproject/opensearch-dashboards 130 | -------------------------------------------------------------------------------- /compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: wodby/postgres:$POSTGRES_TAG 4 | container_name: "${PROJECT_NAME}_postgres" 5 | stop_grace_period: 30s 6 | environment: 7 | POSTGRES_PASSWORD: $DB_PASSWORD 8 | POSTGRES_DB: $DB_NAME 9 | POSTGRES_USER: $DB_USER 10 | # volumes: 11 | # - ./postgres-init:/docker-entrypoint-initdb.d # Place init file(s) here. 12 | # - /path/to/postgres/data/on/host:/var/lib/postgresql/data # I want to manage volumes manually. 13 | 14 | # mariadb: 15 | # image: wodby/mariadb:$MARIADB_TAG 16 | # container_name: "${PROJECT_NAME}_mariadb" 17 | # stop_grace_period: 30s 18 | # environment: 19 | # MYSQL_ROOT_PASSWORD: $DB_ROOT_PASSWORD 20 | # MYSQL_DATABASE: $DB_NAME 21 | # MYSQL_USER: $DB_USER 22 | # MYSQL_PASSWORD: $DB_PASSWORD 23 | # volumes: 24 | # - ./mariadb-init:/docker-entrypoint-initdb.d # Place init .sql file(s) here. 25 | # - /path/to/mariadb/data/on/host:/var/lib/mysql # I want to manage volumes manually. 26 | 27 | python: 28 | build: 29 | context: ./ 30 | args: 31 | PYTHON_TAG: $PYTHON_TAG 32 | container_name: "${PROJECT_NAME}_python" 33 | environment: 34 | DB_HOST: $DB_HOST 35 | DB_USER: $DB_USER 36 | DB_PASSWORD: $DB_PASSWORD 37 | DB_NAME: $DB_NAME 38 | DB_ENGINE: $DB_ENGINE 39 | volumes: 40 | - ./:/usr/src/app:cached 41 | ## Alternative for macOS users: Mutagen https://wodby.com/docs/stacks/python/local#docker-for-mac 42 | # - backend:/usr/src/app 43 | 44 | nginx: 45 | image: wodby/nginx:$NGINX_TAG 46 | container_name: "${PROJECT_NAME}_nginx" 47 | depends_on: 48 | - python 49 | environment: 50 | NGINX_STATIC_OPEN_FILE_CACHE: "off" 51 | NGINX_ERROR_LOG_LEVEL: debug 52 | NGINX_VHOST_PRESET: django 53 | NGINX_BACKEND_HOST: python 54 | # Use this if you don't need Django's media/static locations. 55 | #NGINX_VHOST_PRESET: http-proxy 56 | volumes: 57 | - ./:/usr/src/app:cached 58 | ## Alternative for macOS users: Mutagen https://wodby.com/docs/stacks/python/local#docker-for-mac 59 | # - backend:/usr/src/app 60 | labels: 61 | - "traefik.http.routers.${PROJECT_NAME}_nginx.rule=Host(`${PROJECT_BASE_URL}`)" 62 | 63 | mailpit: 64 | image: axllent/mailpit 65 | container_name: "${PROJECT_NAME}_mailpit" 66 | labels: 67 | - "traefik.http.services.${PROJECT_NAME}_mailpit.loadbalancer.server.port=8025" 68 | - "traefik.http.routers.${PROJECT_NAME}_mailpit.rule=Host(`mailpit.${PROJECT_BASE_URL}`)" 69 | 70 | # varnish: 71 | # image: wodby/varnish:$VARNISH_TAG 72 | # container_name: "${PROJECT_NAME}_varnish" 73 | # depends_on: 74 | # - nginx 75 | # environment: 76 | # VARNISH_SECRET: secret 77 | # VARNISH_BACKEND_HOST: nginx 78 | # VARNISH_BACKEND_PORT: 80 79 | # VARNISH_ALLOW_UNRESTRICTED_PURGE: 1 80 | # labels: 81 | # - "traefik.http.services.${PROJECT_NAME}_varnish.loadbalancer.server.port=6081" 82 | # - "traefik.http.routers.${PROJECT_NAME}_varnish.rule=Host(`varnish.${PROJECT_BASE_URL}`)" 83 | 84 | valkey: 85 | container_name: "${PROJECT_NAME}_valkey" 86 | image: wodby/valkey:$VALKEY_TAG 87 | 88 | # adminer: 89 | # container_name: "${PROJECT_NAME}_adminer" 90 | # image: wodby/adminer:$ADMINER_TAG 91 | # init: true 92 | # environment: 93 | # ADMINER_DEFAULT_DB_DRIVER: pgsql 94 | ## # For MariaDB: 95 | ## ADMINER_DEFAULT_DB_DRIVER: server 96 | # ADMINER_DEFAULT_DB_HOST: $DB_HOST 97 | # ADMINER_DEFAULT_DB_NAME: $DB_NAME 98 | # labels: 99 | # - "traefik.http.routers.${PROJECT_NAME}_adminer.rule=Host(`adminer.${PROJECT_BASE_URL}`)" 100 | 101 | # solr: 102 | # image: wodby/solr:$SOLR_TAG 103 | # container_name: "${PROJECT_NAME}_solr" 104 | # environment: 105 | # SOLR_HEAP: 1024m 106 | # labels: 107 | # - "traefik.http.services.${PROJECT_NAME}_solr.loadbalancer.server.port=8983" 108 | # - "traefik.http.routers.${PROJECT_NAME}_solr.rule=Host(`solr.${PROJECT_BASE_URL}`)" 109 | 110 | # opensearch: 111 | # image: opensearchproject/opensearch:$OPENSEARCH_TAG 112 | # environment: 113 | # "discovery.type": "single-node" 114 | # "plugins.security.disabled": true 115 | # OPENSEARCH_INITIAL_ADMIN_PASSWORD: $OPENSEARCH_ADMIN_PASSWORD 116 | # 117 | # opensearch-dashboards: 118 | # image: opensearchproject/opensearch-dashboards:$OPENSEARCH_TAG 119 | # environment: 120 | # OPENSEARCH_HOSTS: '["http://opensearch:9200"]' 121 | # DISABLE_SECURITY_DASHBOARDS_PLUGIN: true 122 | # depends_on: 123 | # - opensearch 124 | # labels: 125 | # - "traefik.http.services.${PROJECT_NAME}_opensearch.loadbalancer.server.port=5601" 126 | # - "traefik.http.routers.${PROJECT_NAME}_opensearch.rule=Host(`opensearch.${PROJECT_BASE_URL}`)" 127 | 128 | # memcached: 129 | # container_name: "${PROJECT_NAME}_memcached" 130 | # image: wodby/memcached:$MEMCACHED_TAG 131 | 132 | # rsyslog: 133 | # container_name: "${PROJECT_NAME}_rsyslog" 134 | # image: wodby/rsyslog:$RSYSLOG_TAG 135 | 136 | # gotenberg: 137 | # image: gotenberg/gotenberg 138 | # container_name: "${PROJECT_NAME}_gotenberg" 139 | 140 | # node: 141 | # image: wodby/node:$NODE_TAG 142 | # container_name: "${PROJECT_NAME}_node" 143 | # working_dir: /app 144 | # labels: 145 | # - "traefik.http.services.${PROJECT_NAME}_node.loadbalancer.server.port=3000" 146 | # - "traefik.http.routers.${PROJECT_NAME}_node.rule=Host(`node.${PROJECT_BASE_URL}`)" 147 | # expose: 148 | # - "3000" 149 | # volumes: 150 | # - ./path/to/your/single-page-app:/app 151 | # command: sh -c 'npm install && npm run start' 152 | 153 | # opensmtpd: 154 | # container_name: "${PROJECT_NAME}_opensmtpd" 155 | # image: wodby/opensmtpd:$OPENSMTPD_TAG 156 | 157 | traefik: 158 | image: traefik:v2.0 159 | container_name: "${PROJECT_NAME}_traefik" 160 | command: --api.insecure=true --providers.docker 161 | ports: 162 | - '8000:80' 163 | # - '8080:8080' # Dashboard 164 | volumes: 165 | - /var/run/docker.sock:/var/run/docker.sock 166 | 167 | #x-mutagen: 168 | # sync: 169 | # defaults: 170 | # ignore: 171 | # vcs: true 172 | # paths: 173 | # - .DS_Store 174 | # - .history 175 | # - .idea 176 | # backend: 177 | # alpha: "." 178 | # beta: "volume://backend" 179 | # configurationBeta: 180 | # permissions: 181 | # defaultFileMode: 0644 182 | # defaultDirectoryMode: 0755 183 | # defaultOwner: "id:501" 184 | # defaultGroup: "id:20" 185 | 186 | #volumes: 187 | ## Mutagen for macOS users 188 | # backend: 189 | ## For Xdebug profiler 190 | # files: 191 | -------------------------------------------------------------------------------- /docker.mk: -------------------------------------------------------------------------------- 1 | include .env 2 | 3 | .PHONY: up down stop prune ps shell logs 4 | 5 | default: up 6 | 7 | ## help : Print commands help. 8 | help : docker.mk 9 | @sed -n 's/^##//p' $< 10 | 11 | ## up : Start up containers. 12 | up: 13 | @echo "Starting up containers for for $(PROJECT_NAME)..." 14 | docker compose pull 15 | docker compose build 16 | docker compose up -d --remove-orphans 17 | 18 | .PHONY: mutagen 19 | mutagen: 20 | mutagen-compose up 21 | 22 | ## build : Build python image. 23 | build: 24 | @echo "Building python image for for $(PROJECT_NAME)..." 25 | docker compose build 26 | 27 | ## down : Stop containers. 28 | down: stop 29 | 30 | ## start : Start containers without updating. 31 | start: 32 | @echo "Starting containers for $(PROJECT_NAME) from where you left off..." 33 | @docker compose start 34 | 35 | ## stop : Stop containers. 36 | stop: 37 | @echo "Stopping containers for $(PROJECT_NAME)..." 38 | @docker compose stop 39 | 40 | ## prune : Remove containers and their volumes. 41 | ## You can optionally pass an argument with the service name to prune single container 42 | ## prune mariadb : Prune `mariadb` container and remove its volumes. 43 | ## prune mariadb solr : Prune `mariadb` and `solr` containers and remove their volumes. 44 | prune: 45 | @echo "Removing containers for $(PROJECT_NAME)..." 46 | @docker compose down -v $(filter-out $@,$(MAKECMDGOALS)) 47 | 48 | ## ps : List running containers. 49 | ps: 50 | @docker ps --filter name='$(PROJECT_NAME)*' 51 | 52 | ## shell : Access `python` container via shell. 53 | ## You can optionally pass an argument with a service name to open a shell on the specified container 54 | shell: 55 | docker exec -ti -e COLUMNS=$(shell tput cols) -e LINES=$(shell tput lines) $(shell docker ps --filter name='$(PROJECT_NAME)_$(or $(filter-out $@,$(MAKECMDGOALS)), 'python')' --format "{{ .ID }}") sh 56 | 57 | ## logs : View containers logs. 58 | ## You can optinaly pass an argument with the service name to limit logs 59 | ## logs python : View `python` container logs. 60 | ## logs nginx python : View `nginx` and `python` containers logs. 61 | logs: 62 | @docker compose logs -f $(filter-out $@,$(MAKECMDGOALS)) 63 | 64 | # https://stackoverflow.com/a/6273809/1826109 65 | %: 66 | @: -------------------------------------------------------------------------------- /tests/.env: -------------------------------------------------------------------------------- 1 | DB_NAME=python 2 | DB_USER=python 3 | DB_PASSWORD=python 4 | DB_HOST=postgres 5 | DB_ADAPTER=postgresql 6 | DB_ENGINE=django.db.backends.postgresql 7 | 8 | VALKEY_TAG=8-1.0.1 9 | POSTGRES_TAG=17-1.35.6 10 | NGINX_TAG=1.26-5.43.0 11 | 12 | PYTHON_STABILITY_TAG=0.25.0 -------------------------------------------------------------------------------- /tests/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PYTHON_TAG 2 | 3 | FROM wodby/python:${PYTHON_TAG} 4 | 5 | COPY requirements.txt . 6 | 7 | RUN pip install -r requirements.txt 8 | RUN django-admin startproject myapp . 9 | 10 | COPY settings-additions.py /tmp/ 11 | RUN cat /tmp/settings-additions.py >> myapp/settings.py 12 | 13 | ENV GUNICORN_APP myapp.wsgi:application 14 | CMD ["/etc/init.d/gunicorn"] 15 | -------------------------------------------------------------------------------- /tests/compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: wodby/postgres:$POSTGRES_TAG 4 | environment: 5 | POSTGRES_PASSWORD: $DB_PASSWORD 6 | POSTGRES_DB: $DB_NAME 7 | POSTGRES_USER: $DB_USER 8 | 9 | python: 10 | build: 11 | context: ./ 12 | args: 13 | PYTHON_TAG: "${PYTHON_VER}-dev-${PYTHON_STABILITY_TAG}" 14 | environment: 15 | DEBUG: 1 16 | DB_HOST: $DB_HOST 17 | DB_USER: $DB_USER 18 | DB_NAME: $DB_NAME 19 | DB_PASSWORD: $DB_PASSWORD 20 | DB_ENGINE: $DB_ENGINE 21 | volumes: 22 | - codebase:/usr/src/app 23 | - ./tests.sh:/usr/local/bin/tests.sh 24 | depends_on: 25 | - postgres 26 | 27 | valkey: 28 | image: wodby/valkey:$VALKEY_TAG 29 | 30 | nginx: 31 | # image: wodby/nginx:$NGINX_TAG 32 | image: wodby/nginx 33 | environment: 34 | NGINX_STATIC_OPEN_FILE_CACHE: "off" 35 | NGINX_ERROR_LOG_LEVEL: debug 36 | NGINX_VHOST_PRESET: django 37 | NGINX_BACKEND_HOST: python 38 | ports: 39 | - 8080:80 40 | volumes: 41 | - codebase:/var/www/html 42 | depends_on: 43 | - python 44 | 45 | volumes: 46 | codebase: 47 | -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | Django 2 | gunicorn 3 | psycopg2 4 | django-redis -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [[ -n "${DEBUG}" ]]; then 6 | set -x 7 | fi 8 | 9 | docker compose build --force-rm --no-cache python 10 | docker compose up -d 11 | docker compose exec -T postgres make check-ready -f /usr/local/bin/actions.mk max_try=12 wait_seconds=5 12 | 13 | docker compose exec -T python tests.sh 14 | 15 | docker compose down 16 | -------------------------------------------------------------------------------- /tests/settings-additions.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | DATABASES = { 4 | 'default': { 5 | 'ENGINE': os.getenv('DB_ENGINE', 'django.db.backends.sqlite3'), 6 | 'NAME': os.getenv('DB_NAME', os.path.join(BASE_DIR, 'db.sqlite3')), 7 | 'USER': os.getenv('DB_USER', 'user'), 8 | 'PASSWORD': os.getenv('DB_PASSWORD', 'password'), 9 | 'HOST': os.getenv('DB_HOST', 'localhost'), 10 | 'PORT': os.getenv('DB_PORT', '5432'), 11 | } 12 | } 13 | 14 | CACHES = { 15 | "default": { 16 | "BACKEND": "django_redis.cache.RedisCache", 17 | "LOCATION": "redis://valkey:6379/1", 18 | "OPTIONS": { 19 | "CLIENT_CLASS": "django_redis.client.DefaultClient", 20 | } 21 | } 22 | } 23 | 24 | STATIC_ROOT = os.path.join(BASE_DIR, 'static') -------------------------------------------------------------------------------- /tests/tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [[ -n "${DEBUG}" ]]; then 6 | set -x 7 | fi 8 | 9 | python manage.py flush --no-input 10 | python manage.py migrate 11 | python manage.py collectstatic --no-input 12 | python manage.py test 13 | 14 | if [[ "${PYTHON_VERSION}" == 2* ]]; then 15 | django_msg="It worked!" 16 | else 17 | django_msg="Get started with Django" 18 | fi 19 | 20 | curl -s localhost:8080 | grep -q "${django_msg}" 21 | curl -sH "Host: localhost" nginx | grep -q "${django_msg}" 22 | curl -sIH "Host: localhost" nginx/static/admin/css/base.css | grep -q "200 OK" 23 | -------------------------------------------------------------------------------- /traefik.yml: -------------------------------------------------------------------------------- 1 | services: 2 | traefik: 3 | image: traefik:v2.0 4 | command: --api.insecure=true --providers.docker 5 | networks: 6 | - project1 7 | - project2 8 | ports: 9 | - '80:80' 10 | - '8080:8080' 11 | volumes: 12 | - /var/run/docker.sock:/var/run/docker.sock 13 | 14 | networks: 15 | project1: 16 | external: true 17 | name: project1-dir_default 18 | project2: 19 | external: true 20 | name: project2-dir_default 21 | --------------------------------------------------------------------------------