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:
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:
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:
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:
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:
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:
docker-mac-demo/
├── compose.yaml
├── .env
├── .gitignore
└── web/
├── Dockerfile
├── requirements.txt
└── app.py
Create web/requirements.txt:
Flask
PyMySQL
redis
Create web/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:
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:
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:
.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:
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:
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:
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:
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:
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:
SELECT 1;
You can check Redis from its container:
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:
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
| Option | Fit for this Compose workflow | Trade-off |
|---|---|---|
| Local M-series Mac with Docker Desktop | High when you develop at the machine and can keep Docker Desktop available | Uses the local Mac’s resources; services stop being available when the environment is shut down |
| Remote Mac environment | Medium to high when you need a Mac environment accessible away from your desk | Requires a remote-access workflow and a check that the offered environment supports your actual Docker tasks |
| Production server configuration | Low as a copy of this demo | This 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 versionshows a reachable server. - [ ]
docker buildx imagetools inspectand the image documentation confirm that the selected MySQL and Redis tags support the target architecture. - [ ]
docker compose configresolves the file without missing variables or errors. - [ ]
docker compose psshows 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 -vremoves 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.