Manual Docker

Only way: create code yourself, then Docker by hand

You create every PHP or JavaScript file yourself (open the linked files and copy). Then: pullnetwork createvolume create → write Dockerfile → buildrun.

Code enters the container with Dockerfile COPY + docker build. No host bind-mount of source code. No compose up in this guide. Named volumes only for MySQL data + uploads.

Linked code files

Open each link in your editor. Create the same file in your project folder and paste the code.

Node files

package.json server.js Dockerfile

Manual Dockerfile uses npm install (no lock file required).

Order of Docker commands

Dockerfile build flow
You write files → Dockerfile COPY → docker build → image → docker run
  1. Create project folder + every source file (from links above)
  2. docker pull …
  3. docker network create ba-docker-net
  4. docker volume create … (mysql + uploads)
  5. Write Dockerfile piece by piece
  6. docker build -t … .
  7. docker run MySQL → SQLyog SQL
  8. docker run app
  9. CRUD in browser · prove persistence with same volumes
sudo service docker start

PATH A — PHP: create files

1

Folders

mkdir -p ~/ba-docker-php/application
cd ~/ba-docker-php
2

Create each file (open link → copy → paste into nano/VS Code)

Create asOpen this file
application/db.phpphp-app/application/db.php
application/index.phpphp-app/application/index.php
application/create.phpphp-app/application/create.php
application/edit.phpphp-app/application/edit.php
application/delete.phpphp-app/application/delete.php
nano application/db.php
# paste from linked file, save
# repeat for index, create, edit, delete
ls -la application/

Full paste blocks also live in manual-steps.html.

PATH A — PHP: Dockerfile + Docker CLI

3

Dockerfile piece by piece

Reference: php-app/Dockerfile

nano Dockerfile

Piece 1

FROM php:8.2-apache

Piece 2

RUN docker-php-ext-install pdo pdo_mysql

Piece 3 — copy YOUR code into the image (not a mount)

COPY application/ /var/www/html/

Piece 4

RUN mkdir -p /var/www/html/uploads && chown -R www-data:www-data /var/www/html/uploads
4

Pull · network · volumes · build · run

docker pull mysql:8.0
docker pull php:8.2-apache
docker images

docker network create ba-docker-net
docker volume create ba-docker-mysql
docker volume create ba-docker-uploads
docker volume ls
docker volume inspect ba-docker-mysql

cd ~/ba-docker-php
docker build -t ba-docker-php .

docker run -d \
  --name ba-docker-db \
  --network ba-docker-net \
  --network-alias db \
  -e MYSQL_ROOT_PASSWORD=rootpass \
  -v ba-docker-mysql:/var/lib/mysql \
  -p 3306:3306 \
  mysql:8.0

# wait: docker logs -f ba-docker-db
# then SQLyog port 3306 → run setup SQL

docker run -d \
  --name ba-docker-app \
  --network ba-docker-net \
  -e DB_HOST=db -e DB_NAME=userdb -e DB_USER=appuser -e DB_PASS=apppass \
  -v ba-docker-uploads:/var/www/html/uploads \
  -p 8080:80 \
  ba-docker-php

Browser: http://localhost:8080

docker run --rm -v ba-docker-uploads:/data alpine ls -la /data
docker network inspect ba-docker-net

PATH B — Node: create files

1

Folder + files

mkdir -p ~/ba-docker-node
cd ~/ba-docker-node
Create asOpen this file
package.jsonnodejs-app/package.json
server.jsnodejs-app/server.js
nano package.json   # paste from link
nano server.js      # paste entire linked file
ls -la

PATH B — Node: Dockerfile + Docker CLI

2

Dockerfile pieces

FROM node:22-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --omit=dev
COPY server.js ./
RUN mkdir -p /app/uploads
EXPOSE 3000
CMD ["node", "server.js"]
3

Pull · volumes · build · run

docker pull mysql:8.0
docker pull node:22-alpine

docker network create ba-docker-net   # if not exists
docker volume create ba-docker-mysql-node
docker volume create ba-docker-uploads-node

# if PHP containers still running:
docker rm -f ba-docker-app ba-docker-db

cd ~/ba-docker-node
docker build -t ba-docker-node .

docker run -d --name ba-docker-db --network ba-docker-net --network-alias db \
  -e MYSQL_ROOT_PASSWORD=rootpass \
  -v ba-docker-mysql-node:/var/lib/mysql \
  -p 3307:3306 mysql:8.0

# SQLyog port 3307 → same SQL

docker run -d --name ba-docker-app --network ba-docker-net \
  -e DB_HOST=db -e DB_NAME=userdb -e DB_USER=appuser -e DB_PASS=apppass -e PORT=3000 \
  -v ba-docker-uploads-node:/app/uploads \
  -p 3000:3000 ba-docker-node

Browser: http://localhost:3000

Database (required after MySQL is running)

!

Apps do not create the database. You create it in SQLyog.

Full steps: setup-database.html · raw SQL: setup.sql

PHPNode
Host127.0.0.1127.0.0.1
User / passroot / rootpassroot / rootpass
Port33063307
CREATE DATABASE IF NOT EXISTS userdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'appuser'@'%' IDENTIFIED BY 'apppass';
GRANT ALL PRIVILEGES ON userdb.* TO 'appuser'@'%';
FLUSH PRIVILEGES;
USE userdb;
CREATE TABLE IF NOT EXISTS users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(100) NOT NULL UNIQUE,
  password VARCHAR(255) NOT NULL,
  file_name VARCHAR(255) DEFAULT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Persistence proof & cleanup

Persistence
# Remove containers only — named volumes stay
docker rm -f ba-docker-app ba-docker-db

# Start again with the SAME -v volume names
# (re-run the docker run commands from your path)

# Prove uploads volume:
docker run --rm -v ba-docker-uploads:/data alpine ls -la /data

Cleanup

docker rm -f ba-docker-app ba-docker-db
docker volume rm ba-docker-mysql ba-docker-uploads
# or node: ba-docker-mysql-node ba-docker-uploads-node
docker network rm ba-docker-net
docker rmi ba-docker-php   # or ba-docker-node

After you change code

Edit the file on disk → docker build -t … . again → docker rm -f ba-docker-appdocker run app again.
Code is not live-mounted; it only updates when you rebuild.

Full step-by-step with every paste block

manual-steps.html has every file’s full code, every command, and the file links again.

Use that as your main checklist while typing.