{"id":23544359,"url":"https://github.com/sergelogvinov/devops-examples","last_synced_at":"2026-01-28T17:33:14.501Z","repository":{"id":255660687,"uuid":"851185642","full_name":"sergelogvinov/devops-examples","owner":"sergelogvinov","description":"Devops opinionated best practices","archived":false,"fork":false,"pushed_at":"2024-10-30T19:12:37.000Z","size":53,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-10T15:56:15.241Z","etag":null,"topics":["cicd","devops","dockerfile","github-actions","makefile","taskfile"],"latest_commit_sha":null,"homepage":"","language":"Dockerfile","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/sergelogvinov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null}},"created_at":"2024-09-02T15:29:35.000Z","updated_at":"2024-10-31T06:56:55.000Z","dependencies_parsed_at":"2024-10-30T20:33:40.475Z","dependency_job_id":null,"html_url":"https://github.com/sergelogvinov/devops-examples","commit_stats":null,"previous_names":["sergelogvinov/devops-examples"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sergelogvinov/devops-examples","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergelogvinov%2Fdevops-examples","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergelogvinov%2Fdevops-examples/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergelogvinov%2Fdevops-examples/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergelogvinov%2Fdevops-examples/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sergelogvinov","download_url":"https://codeload.github.com/sergelogvinov/devops-examples/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergelogvinov%2Fdevops-examples/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28847814,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-28T15:15:36.453Z","status":"ssl_error","status_checked_at":"2026-01-28T15:15:13.020Z","response_time":57,"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":["cicd","devops","dockerfile","github-actions","makefile","taskfile"],"created_at":"2024-12-26T07:14:58.804Z","updated_at":"2026-01-28T17:33:14.482Z","avatar_url":"https://github.com/sergelogvinov.png","language":"Dockerfile","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Devops opinionated best practices\n\nI've been working in the DevOps field for a while now and notice that many companies are repeating the same mistakes. This document is a collection of best practices that I've found to be useful in my work. It's opinionated, and it's not meant to be a comprehensive guide to DevOps. Instead, it's a collection of practices that I've found to be useful in my work.\n\nAll ideas are working for mono-repo projects and for microservices.\n\n## Table of Contents\n\n- [Build projects](#build-projects)\n- [Continuous Integration](#continuous-integration)\n- [Tools](#tools)\n\nProject examples:\n\n- [Golang](golang/)\n- [Python](python/)\n- [Symfony](symfony/) PHP-fpm, Nginx\n\n## Build projects\n\nSince `docker` is the most popular containerization tool, I recommend using it for building projects. It's easy to use, and it's widely supported. Docker has alternatives like Podman, but most of the code are compatible with Docker. I prefer the `BuildKit` extension for Docker because it's faster and more efficient than the standard Docker build process. Also, it's supported multi architecture builds.\n\nBuildkit allows you to use cache volumes, which is useful for caching dependencies between builds.\nIt's faster than the standard Docker build process because it doesn't need to download the dependencies every time you build the project.\nThink of the images layers as a cache. If you change the code, the layer with the code will be invalidated, and the next layer will be rebuilt.\n\nHere's a simple Dockerfile that you can use to build your projects:\n\n```Dockerfile\n# syntax = docker/dockerfile:1.10\n########################################\n\nFROM registry.k8s.io/pause:3.8 AS pause\n\n########################################\n#\n# Base image\n#\n\nFROM python AS base\n\n# Basic requirements for the environment\nENV DEBIAN_FRONTEND=noninteractive TERM=xterm-color LC_ALL=C.UTF-8 LANG=C.UTF-8\nENV PYTHONUNBUFFERED=1 POETRY_VIRTUALENVS_CREATE=false\n\n# Install basic packages and create a non-root user\nRUN --mount=type=cache,id=apt-cache-python,target=/var/cache/apt,sharing=locked \\\n    LC_ALL=C apt-get update -y \u0026\u0026 \\\n    LC_ALL=C apt-get install -y --no-install-recommends locales ca-certificates mime-support make libpq5 vim gettext procps \u0026\u0026 \\\n    sed -i 's/^# *\\(en_US.UTF-8\\)/\\1/' /etc/locale.gen \u0026\u0026 LC_ALL=C locale-gen \u0026\u0026 \\\n    apt-get autoremove -y \u0026\u0026 \\\n    rm -rf /var/lib/apt/lists/* /tmp/* \u0026\u0026 \\\n    useradd -ms /bin/bash --uid 5000 -d /www/app app\n\n########################################\n#\n# Build the project with development dependencies\n#\n\nFROM base AS builder\n\n# Dependencies for building the python packages\nRUN --mount=type=cache,id=apt-cache-python,target=/var/cache/apt,sharing=locked \\\n    apt-get update \u0026\u0026 apt-get install -y --no-install-recommends build-essential python3-dev libpq-dev git \u0026\u0026 \\\n    pip3 install poetry \u0026\u0026 \\\n    rm -rf /var/lib/apt/lists/* /tmp/*\n\nWORKDIR /www/app\n\n# Install the project dependencies\n# They don't change often, so we can cache them\nCOPY --chown=app:app [\"myservice/poetry.lock\",\"myservice/pyproject.toml\",\"/www/app/\"]\nRUN --mount=type=cache,id=poetry,target=/root/.cache poetry install --no-interaction --no-root \u0026\u0026 \\\n    rm -rf /tmp/*\n\n########################################\n#\n# Copy necessary files to the release image\n#\n\nFROM base AS release\n\nENV PYTHONPATH=/www/shared-apps PATH=/home/app/.local/bin:/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n\n# Pause binary for run it for testing purposes\n# It shuts down immediately, and has small size\nCOPY --from=pause /pause /pause\n# Python packages\nCOPY --from=builder --chown=app:app /usr/local /usr/local\n# Project files\nCOPY                --chown=app:app myservice/ /www/app/\n\n# Switch to the non-root user\nUSER app\nWORKDIR /www/app\n\nCMD [\"bash\"]\n```\n\n## Continuous Integration\n\nThere are many CI/CD tools available, but all of them have they own vendor lock-in. Which means that if you start using a specific tool, it's hard to switch to another CICD. But they all have the same basic features, like bash, docker, and git. So, better to use shell scripts for your CI/CD pipelines and store them in your repository. Just run the shell scripts in your CI/CD specific rulesets.\n\n### Build process\n\nHow to shoose the right name of the shell scripts?\n1. The world get used to the `Makefile`, `Taskfile`. Makefile is the most popular, but it's not easy to read and write. So, I recommend using the `Taskfile` format. It's easy to read and write, and it's supported by most of the CI/CD tools.\n1. Put the scripts in the root of the repository. In mono-repo projects, store the scripts in project folders, and one script on the root of the repository to build all the projects.\n1. Create a `README.md` file in the root of the repository and explain how to run the scripts (basic commands).\n1. Do not forget to add `help` command to the scripts. It's useful for the developers to understand what the script does.\n\nExample:\n\n`make help` output:\n\n```shell\n# Getting Started\n\nTo build this project, you must have the following installed:\n\n- git\n- make\n- golang 1.20+\n- golangci-lint\n\nhelp                           This help menu\nclean                          Clean\nbuild                          Build\nrun                            Run\nlint                           Lint Code\nunit                           Unit Tests\n```\n\n### Testing process\n\nIf you code depends on other services, like databases, queues, etc., you should use the `docker-compose` tool to run the services in the CI/CD pipeline. It's easy to use, and do not forget to add `health checks` to the services. It's useful to wait for the services to be ready before running the tests.\n\nTo simplify the process, I recommend creating a `base` service in the `docker-compose` file. It's a service that creates a network for the other services. So, all services will use localhost to connect to each other.\n\nExample:\n\n```yaml\n# docker-compose.yml\n\nservices:\n  # It creates a network for the services\n  base:\n    image: registry.k8s.io/pause:3.8\n\n  postgres:\n    image: ghcr.io/sergelogvinov/postgresql:15.6\n    shm_size: 1g\n    # It uses the network created by the base service\n    network_mode: \"service:base\"\n    # Disable fsync to speed up the tests\n    command: -c fsync=off\n    # Default user and password\n    environment:\n      - POSTGRES_USER=myservice\n      - POSTGRES_PASSWORD=myservice\n      - POSTGRES_DB=myservice\n\n    # Docker will wait for the service to be ready before continuing (depends_on)\n    healthcheck:\n      test: [\"CMD-SHELL\", \"psql -U myservice -d myservice -c 'SELECT 1'\"]\n      interval: 10s\n      timeout: 5s\n      retries: 10\n      start_period: 10s\n\n  test:\n    build:\n      context: .\n      target: test\n      dockerfile: myservice/Dockerfile\n    # It uses the network created by the base service\n    network_mode: \"service:base\"\n    # Run the container, than we can run the tests inside the container\n    command: /pause\n    depends_on:\n      - postgres\n```\n\nBuild and run the tests, will look like this:\n\n```shell\ndocker compose -f docker-compose.yml build\ndocker compose -f docker-compose.yml up -d --wait\ndocker compose -f docker-compose.yml exec test my-project-test\n```\n\n### GitHub Actions\n\nExample of the GitHub Actions workflow:\n\n```yaml\nname: Build\non:\n  # Run the workflow on push to the main branch\n  push:\n    branches:\n      - main\n\njobs:\n  build-publish:\n    name: \"Build image and publish\"\n    # Limit the time for the job, sometimes something goes wrong\n    timeout-minutes: 15\n    runs-on: ubuntu-latest\n    permissions:\n      # Define the permissions, packages needs to push the images to the github registry\n      contents: read\n      packages: write\n\n    # Some steps are grouped, it helps to understand the workflow\n    # Checkout process, prepare the environment, build and push the images\n    steps:\n      # Checkout the code to the github runner\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      # For multi architecture builds, we need to set up QEMU and docker buildx\n      - name: Set up QEMU\n        uses: docker/setup-qemu-action@v3\n      - name: Set up docker buildx\n        uses: docker/setup-buildx-action@v3\n\n      # Build and push the images\n      #  We run Makefile, which has the build and push commands\n      #  The env PUSH=true, tells the Makefile to push the image results to the registry\n      #  Do not forget to set timeout for the job\n      - name: Build and push\n        timeout-minutes: 10\n        run: make images\n        env:\n          PUSH: \"true\"\n```\n\n## Tools\n\n* [Docker](https://www.docker.com/)\n* [BuildKit](https://github.com/moby/buildkit)\n* [Docker Compose](https://docs.docker.com/compose/)\n* [Dive](https://github.com/wagoodman/dive)\n* [Taskfile](https://taskfile.dev/)\n* [Makefile](https://www.gnu.org/software/make/)\n\nSelf hosted CI/CD agents/conrollers:\n\n* [TeamCity](https://github.com/sergelogvinov/containers/tree/main/teamcity)\n* [Githab Actions](https://github.com/sergelogvinov/containers/tree/main/github-actions-runner)\n\n## Other resources\n\n* [Understanding Docker](https://dev.to/aurelievache/understanding-docker-part-1-retrieve-pull-images-3ccn)\n* [Dockerfile reference](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergelogvinov%2Fdevops-examples","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsergelogvinov%2Fdevops-examples","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergelogvinov%2Fdevops-examples/lists"}