CI/CD ·

How to Run Docker on a Mac: Install Docker Desktop on M-Series Chips and Deploy MySQL, Redis, and a Web Project

How to Run Docker on a Mac: Install Docker Desktop on M-Series Chips and Deploy MySQL, Redis, and a Web Project

This guide is for developers setting up a local Docker environment on an M-series Mac. Install and verify Docker Desktop, build a small web app, and use Compose to run it with MySQL and Redis. The examples also show how to check image compatibility, service access, persistence, and common startup failures.

Docker’s Mac installation guide provides separate downloads for Apple silicon and Intel Macs. Choose the Apple silicon version on an M-series Mac, then verify each image’s architecture before starting your services. You can run Docker Desktop and use Docker Compose to connect a web app to MySQL and Redis; the essential checks are image compatibility, service health, port mapping, and persistent volumes. Docker’s Mac installation guide

Fit: suitable if you want a repeatable local development stack and your Mac meets Docker Desktop’s current system requirements. Not suitable by default if the machine must be available remotely at all times, or if your workflow depends on hardware access that a virtualized container environment cannot provide.

This walkthrough is for developers setting up Docker for the first time on an M-series Mac; full-stack and backend developers who need a web app, MySQL, and Redis together; and teams deciding whether local or remote Mac development better fits their workflow.

How to run Docker on a Mac with Docker Desktop

Docker Desktop runs Linux containers through a Linux virtual machine on macOS. Apple’s documentation describes how its virtualization framework runs Linux in a virtual machine; the practical consequence is that a container’s Linux environment is not the same thing as running a native macOS process. Apple’s Linux virtual machine documentation

Before installing, check Docker’s current macOS requirements and select its Apple silicon download. Requirements and download options can change, so use the official installation guide rather than relying on a screenshot or an old setup article. Complete the installer’s prompts and approve any requested permissions. Start Docker Desktop and wait until its interface reports that the engine is running.

How do you install Docker Desktop and confirm Docker is running on an M-series Mac? Install the Apple silicon build, open Docker Desktop, and check both the command-line client and the server. From Terminal, run:

bash
docker version
docker info

docker version should show both a client and a server section. If it only shows client details or reports that it cannot connect to the daemon, Docker Desktop may still be starting, or the engine may not be running. Check the Docker Desktop status before changing configuration.

For a basic container check, run:

bash
docker run --rm hello-world

The command asks Docker to start a small test container and remove it when it exits. A successful result confirms that the client can reach the engine and run a container. It does not prove that every application image you need supports Apple Silicon; check those images separately.

You can also confirm that Compose is available:

bash
docker compose version

Use the current Compose command form shown here. The space between docker and compose matters; do not substitute an older command from an unrelated tutorial without checking which Compose version is installed.

Check image architecture before starting a project

An M-series chip uses Apple Silicon architecture. Container images can publish support for more than one architecture, but availability varies by image and tag. Docker’s multi-platform documentation explains how image platforms are represented and inspected. Docker’s multi-platform build and image documentation

Inspect the image manifests before you rely on an image for your project:

bash
docker buildx imagetools inspect mysql:8.4
docker buildx imagetools inspect redis:7

These commands display the platforms listed for those image tags. Compare the output with the architecture available on your Mac. Also check the image’s official documentation: the MySQL image notes and Redis image notes describe their respective images and usage.

How can you tell whether an image supports an M-series Mac? Look for an Apple Silicon-compatible platform in the image manifest, then confirm the tag’s support in the image maintainer’s documentation. If a container fails to start, inspect its logs and the error message before adding a platform override. An override can request a different platform, but it does not make an unsupported image reliable or eliminate the cost of emulation.

Use these checks when a service exits unexpectedly:

bash
docker compose ps
docker compose logs db
docker compose logs redis

Replace the service name with the one reported in your Compose file. A manifest check tells you what the image publishes; the container status and logs tell you what happened when Docker tried to run it.

Build a small web service and compose the stack

The example uses a small Flask application. It asks MySQL to return a simple query and writes and reads a value in Redis. That makes it possible to check the complete connection path without adding a framework or a separate database schema.

Create this directory layout:

text
docker-mac-demo/
├── compose.yaml
├── .env
├── .gitignore
└── web/
    ├── Dockerfile
    ├── requirements.txt
    └── app.py

Create web/requirements.txt:

text
Flask
PyMySQL
redis

Create web/Dockerfile:

dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 8000
CMD ["python", "app.py"]

Create web/app.py:

python
import os

from flask import Flask
import pymysql
import redis

app = Flask(__name__)

@app.get("/")
def index():
    db = pymysql.connect(
        host=os.environ["DB_HOST"],
        port=int(os.environ["DB_PORT"]),
        user=os.environ["DB_USER"],
        password=os.environ["DB_PASSWORD"],
        database=os.environ["DB_NAME"],
        connect_timeout=3,
    )
    try:
        with db.cursor() as cursor:
            cursor.execute("SELECT 1")
            mysql_result = cursor.fetchone()[0]
    finally:
        db.close()

    cache = redis.Redis(
        host=os.environ["REDIS_HOST"],
        port=int(os.environ["REDIS_PORT"]),
        decode_responses=True,
    )
    cache.set("docker-demo", "connected")
    redis_result = cache.get("docker-demo")

    return {
        "mysql": mysql_result,
        "redis": redis_result,
        "status": "connected",
    }

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

Create a root .env file for local development:

dotenv
MYSQL_DATABASE=appdb
MYSQL_USER=appuser
MYSQL_PASSWORD=replace-with-a-local-secret
MYSQL_ROOT_PASSWORD=replace-with-a-different-local-secret

Add .env to .gitignore:

text
.env

Do not commit these example credentials, reuse them in production, or treat a local .env file as a production secret-management system.

Now create compose.yaml:

yaml
services:
  db:
    image: mysql:8.4
    environment:
      MYSQL_DATABASE: ${MYSQL_DATABASE:?Set MYSQL_DATABASE in .env}
      MYSQL_USER: ${MYSQL_USER:?Set MYSQL_USER in .env}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD:?Set MYSQL_PASSWORD in .env}
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?Set MYSQL_ROOT_PASSWORD in .env}
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "mysqladmin ping -h 127.0.0.1 -uroot -p\"$${MYSQL_ROOT_PASSWORD}\" --silent"
        ]
      interval: 5s
      timeout: 3s
      retries: 20

  redis:
    image: redis:7
    command: ["redis-server", "--appendonly", "yes"]
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 20

  web:
    build:
      context: ./web
    environment:
      DB_HOST: db
      DB_PORT: "3306"
      DB_NAME: ${MYSQL_DATABASE:?Set MYSQL_DATABASE in .env}
      DB_USER: ${MYSQL_USER:?Set MYSQL_USER in .env}
      DB_PASSWORD: ${MYSQL_PASSWORD:?Set MYSQL_PASSWORD in .env}
      REDIS_HOST: redis
      REDIS_PORT: "6379"
    ports:
      - "8080:8000"
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy

volumes:
  mysql_data:
  redis_data:

The Compose file defines each service, its environment, connections, checks, and storage. Compose provides service-name networking, so the web container connects to db and redis; it does not use localhost to reach those sibling containers. The ports entry publishes the web app to the Mac. MySQL and Redis need no host-published ports for this example because only the web container connects to them.

The named volumes keep database files outside the containers’ writable layers. Health checks and depends_on with service_healthy make Compose wait for the dependencies’ health checks before starting the web service. Read Docker’s Compose service configuration reference and startup-order guidance for the behavior behind these settings.

How do you start MySQL and Redis together with Docker Compose? Save the files, confirm that .env is present, and run these commands from the project root:

bash
docker compose config
docker compose up --build -d
docker compose ps

docker compose config resolves the Compose file and environment substitutions before launch. If it reports a missing variable, correct .env instead of deleting the required-value checks. docker compose up --build -d builds the web image and starts the services in the background. docker compose ps shows whether each service is running and whether its health check has passed.

If startup fails, read the service logs rather than repeatedly restarting the stack:

bash
docker compose logs --tail=100 db
docker compose logs --tail=100 redis
docker compose logs --tail=100 web

The MySQL team also documents a Docker-based getting-started flow in its MySQL Docker guide.

Verify the web app, database, and stored data

Open http://localhost:8080 in a browser, or check the response from Terminal:

bash
curl http://localhost:8080

A successful response includes a MySQL result of 1, a Redis value of connected, and a connected status. These results verify that the web container can reach both services using Compose’s internal service names. If the browser cannot connect, inspect docker compose ps and confirm that the web service publishes host port 8080 to container port 8000.

To check the database directly, run a MySQL client inside the database container:

bash
docker compose exec db mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"

The environment variables in your shell may not be loaded from .env automatically. If this command does not find them, enter the database container with docker compose exec db mysql -u appuser -p appdb and provide the local password when prompted, substituting the user and database values from .env. At the MySQL prompt, run:

sql
SELECT 1;

You can check Redis from its container:

bash
docker compose exec redis redis-cli GET docker-demo

The expected value is connected after visiting the web route. This confirms a write and read through the app’s Redis connection. A missing value can mean the route has not run, the app cannot connect, or the Redis service has restarted without persistent data.

To check persistence, first visit the web page so the app writes its Redis value. Then stop and recreate the services without removing named volumes:

bash
docker compose down
docker compose up -d

Visit the page again and check the MySQL and Redis responses. The volume declarations in this example keep service data when containers are removed and recreated. By contrast, docker compose down -v removes the Compose-managed volumes too; do not use it when you intend to retain local database data. Keep a separate backup if the data matters.

A local development stack is not a production deployment. The sample credentials, open development server, image tags, and minimal checks are for a reproducible local exercise, not a complete security, backup, or availability plan.

Troubleshoot failures by checking evidence

The image fails with an architecture or platform error. Inspect the image manifest with docker buildx imagetools inspect, then compare the result with the image’s official support notes. Check the failing container’s logs with docker compose logs SERVICE. Only consider a platform override if the image maintainer documents that route and you understand its limitations.

A port is already in use. Check the ports mapping in compose.yaml and the exact error in docker compose logs web. If another local process occupies host port 8080, change only the host side of the mapping—for example, use another available host port while leaving the container port at 8000. Then recreate the service and use the new host port in the browser. MySQL and Redis do not need host port mappings for this stack’s web-to-service traffic.

MySQL exits or the app reports an authentication error. Review docker compose logs db and docker compose logs web. Check that the values for database name, user, and password match in .env and the Compose environment. If you change MySQL initialization variables after the database has already initialized on a named volume, changing the environment alone may not reset the existing database account. Preserve the volume if its contents matter; back it up before considering a reset.

The web service starts before a dependency is ready. Inspect docker compose ps for health status and check the database or Redis logs. This configuration uses health checks with service_healthy; a running container is not necessarily a ready service. Fix the failed health check or the underlying startup error before changing the web app’s connection settings.

Data disappears after a restart. Confirm that mysql_data and redis_data are declared and mounted at the paths used by the images. Check whether the command used to stop the stack included -v. A container restart and a volume removal are different operations: the latter can delete the persisted development data.

Choose local or remote Mac based on the workflow

OptionFit for this Compose workflowTrade-off
Local M-series Mac with Docker DesktopHigh when you develop at the machine and can keep Docker Desktop availableUses the local Mac’s resources; services stop being available when the environment is shut down
Remote Mac environmentMedium to high when you need a Mac environment accessible away from your deskRequires a remote-access workflow and a check that the offered environment supports your actual Docker tasks
Production server configurationLow as a copy of this demoThis sample does not provide production-grade secrets, backups, monitoring, or deployment controls

The ratings are workflow guidance, not performance measurements. Before choosing a remote environment, confirm its actual specifications, access method, and Docker suitability. The ZekVPS Mac environment information is the place to check what is currently offered; do not assume unlisted capabilities.

Use this completion list before relying on the environment:

  • [ ] Docker Desktop is installed using the Apple silicon download, and docker version shows a reachable server.
  • [ ] docker buildx imagetools inspect and the image documentation confirm that the selected MySQL and Redis tags support the target architecture.
  • [ ] docker compose config resolves the file without missing variables or errors.
  • [ ] docker compose ps shows the database and Redis healthy and the web service running.
  • [ ] The web route returns a MySQL response and a Redis read.
  • [ ] The named volumes are present, and the team knows that docker compose down -v removes them.

If all checks pass and you work at the Mac, a local Docker Desktop setup is usually the more direct development path. If the Mac must remain accessible remotely, or local availability is a constraint, compare the requirements against the actual remote Mac options before moving the workflow. Review ZekVPS Mac support information and the current environment details, then choose remote access only if they match the project’s needs.

Run Your Docker Workloads on a Dedicated Mac

Rent a ZekVPS Mac mini M4 with full macOS to build and test Docker projects remotely.

Use SSH or VNC to manage your development environment from your own computer.

If you are moving MCP or Agents from demo to daily use, a snapshot-ready cloud Mac node beats swapping frameworks again. View ZekVPS cloud Mac mini plans — Separate lab from daily driver for calmer deployments.

Limited offer