Complete VPS environment running locally on Mac M1 with all premium features.
- β SSH works properly
- β No volume conflicts
- β Persistent data storage
- β All services stable
- β Proper user management
- β Security features enabled
Save these files in your project directory (~/local-vps):
~/local-vps/
βββ docker-compose.yml # Main configuration
βββ Dockerfile # VPS image definition
βββ entrypoint.sh # Container startup script
βββ .env # Passwords (change these!)
βββ .gitignore # Git ignore file
βββ setup.sh # Setup script
βββ prometheus/
β βββ prometheus.yml # Monitoring config
βββ shared/ # Shared folder with Mac
# 1. Create directory
mkdir ~/local-vps && cd ~/local-vps
# 2. Save all 7 files from the artifacts above
# 3. Make scripts executable
chmod +x setup.sh entrypoint.sh
# 4. IMPORTANT: Edit .env and change passwords
nano .env
# 5. Run setup
./setup.sh# As root
ssh root@localhost -p 2222
# Password: ChangeMe123! (or what you set in .env)
# As admin user
ssh admin@localhost -p 2222
# Password: Admin123! (or what you set in .env)# As root
docker exec -it my-vps bash
# As admin user
docker exec -it -u admin my-vps bash- Portainer (Docker GUI): https://localhost:9443
- Grafana (Monitoring): http://localhost:3002
- User: admin / GrafanaAdmin123!
- Prometheus (Metrics): http://localhost:9090
# From your Mac
psql -h localhost -p 5432 -U pgadmin -d maindb
# Password: PostgresSecure123!
# From inside VPS
apt install postgresql-client
psql -h postgres -U pgadmin -d maindb# From your Mac
mysql -h 127.0.0.1 -P 3306 -u root -p
# Password: MysqlRoot123!
# From inside VPS
apt install mysql-client
mysql -h mysql -u root -p# From your Mac
redis-cli -h localhost -p 6379 -a RedisSecure123!
# From inside VPS
apt install redis-tools
redis-cli -h redis -a RedisSecure123!# From inside VPS
apt install mongodb-clients
mongosh mongodb://mongoadmin:MongoSecure123!@mongodb:27017ssh root@localhost -p 2222
# Install
apt update
apt install -y nginx
# Start
systemctl start nginx
systemctl enable nginx
# Test
curl localhost
# On your Mac browser: http://localhost# Install Node.js 20
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs
# Verify
node --version
npm --version
# Install PM2 for process management
npm install -g pm2
# Create test app
mkdir -p /opt/myapp && cd /opt/myapp
cat > server.js << 'EOF'
const http = require('http');
http.createServer((req, res) => {
res.end('Hello from Node.js!');
}).listen(3000, () => console.log('Running on :3000'));
EOF
# Run with PM2
pm2 start server.js --name myapp
pm2 save
pm2 startup
# Test: http://localhost:3000# Install PHP 8.1
apt install -y php8.1-fpm php8.1-cli php8.1-mysql \
php8.1-curl php8.1-xml php8.1-mbstring
# Start
systemctl start php8.1-fpm
systemctl enable php8.1-fpm
# Test
php --versionapt install -y python3 python3-pip python3-venv
python3 --version# Inside VPS
ssh root@localhost -p 2222
# Change root password
passwd
# Change admin password
passwd admin# On your Mac, generate SSH key
ssh-keygen -t ed25519 -C "your_email@example.com"
# Copy to VPS
ssh-copy-id -p 2222 admin@localhost
# Test key login
ssh -p 2222 admin@localhost
# Disable password auth (inside VPS)
nano /etc/ssh/sshd_config
# Set: PasswordAuthentication no
systemctl restart ssh
# Disable root login
# Set: PermitRootLogin no# Inside VPS
ufw enable
ufw status
# The following ports are already configured:
# 22 (SSH), 80 (HTTP), 443 (HTTPS)
# 3000, 3001, 8080 (App ports)# Check fail2ban status
fail2ban-client status
fail2ban-client status sshd
# View banned IPs
fail2ban-client get sshd banned# Backup web files
docker exec my-vps tar -czf /shared/www-backup.tar.gz /var/www
# Backup configs
docker exec my-vps tar -czf /shared/config-backup.tar.gz /etc/nginx
# Backup PostgreSQL
docker exec vps-postgres pg_dump -U pgadmin maindb > ~/local-vps/shared/postgres.sql
# Backup MySQL
docker exec vps-mysql mysqldump -u root -p${MYSQL_ROOT_PASSWORD} appdb > ~/local-vps/shared/mysql.sql
# Files are now in: ~/local-vps/shared/# Restore web files
docker exec my-vps tar -xzf /shared/www-backup.tar.gz -C /
# Restore PostgreSQL
cat ~/local-vps/shared/postgres.sql | docker exec -i vps-postgres psql -U pgadmin maindb
# Restore MySQL
cat ~/local-vps/shared/mysql.sql | docker exec -i vps-mysql mysql -u root -p${MYSQL_ROOT_PASSWORD} appdb# Check status
docker-compose ps
# View logs
docker-compose logs -f my-vps
docker-compose logs -f postgres
# Restart services
docker-compose restart my-vps
docker-compose restart
# Stop all
docker-compose stop
# Start all
docker-compose start
# Remove all (keeps data)
docker-compose down
# Remove all + DELETE DATA
docker-compose down -v
# Rebuild from scratch
docker-compose down
docker-compose build --no-cache
docker-compose up -dMac M1 Host (Your Computer)
β
ββ localhost:2222 β my-vps:22 (SSH)
ββ localhost:80 β my-vps:80 (HTTP)
ββ localhost:443 β my-vps:443 (HTTPS)
ββ localhost:3000 β my-vps:3000 (App)
ββ localhost:5432 β postgres:5432
ββ localhost:3306 β mysql:3306
ββ localhost:6379 β redis:6379
ββ localhost:27017 β mongodb:27017
ββ localhost:9443 β portainer:9443
ββ localhost:3002 β grafana:3000
ββ localhost:9090 β prometheus:9090
Docker Network (172.20.0.0/16)
ββ 172.20.0.10 - my-vps
ββ 172.20.0.20 - postgres
ββ 172.20.0.21 - mysql
ββ 172.20.0.22 - redis
ββ 172.20.0.23 - mongodb
ββ 172.20.0.30 - portainer
ββ 172.20.0.31 - prometheus
ββ 172.20.0.32 - grafana
ββ 172.20.0.33 - node-exporter
Inside VPS, databases are accessible by name:
postgres:5432mysql:3306redis:6379mongodb:27017
# Check if container is running
docker ps | grep my-vps
# Check SSH status inside container
docker exec my-vps ps aux | grep sshd
# Restart container
docker-compose restart my-vps
# View logs
docker logs my-vps# Check what's using the port
lsof -i :2222
# Change port in docker-compose.yml
# "2223:22" instead of "2222:22"# Check logs
docker logs my-vps
# Try starting without detached mode to see errors
docker-compose up
# Rebuild clean
docker-compose down
docker-compose build --no-cache
docker-compose up -d# Access directly
docker exec -it my-vps bash
# Change password
passwd root
passwd admin# WARNING: This deletes ALL data
docker-compose down -v
rm -rf shared/*
./setup.sh- Linux: https://linux.die.net/
- Nginx: https://nginx.org/en/docs/
- Docker: https://docs.docker.com/
- Security: https://www.cisecurity.org/
- Changed all passwords in .env
- Changed root password:
passwd - Changed admin password:
passwd admin - Set up SSH key authentication
- Disabled password SSH authentication
- Disabled root SSH login
- Enabled UFW firewall:
ufw enable - Tested fail2ban:
fail2ban-client status - Set up regular backups
- Configured monitoring in Grafana
- Linux Administration: Users, permissions, services
- Web Servers: Nginx, Apache configuration
- Application Deployment: Node.js, PHP, Python apps
- Database Management: SQL, NoSQL, backups
- Security: Firewall, SSH hardening, intrusion detection
- Monitoring: Prometheus, Grafana dashboards
- Networking: Ports, proxies, load balancing
- DevOps: Docker, containers, orchestration
- Deploy a real application (WordPress, Ghost, custom app)
- Set up Nginx as reverse proxy for multiple apps
- Configure SSL with self-signed certificates
- Create automated backup scripts with cron
- Set up monitoring dashboards in Grafana
- Learn Docker Compose for multi-container apps
- Practice security hardening
- Experiment with CI/CD pipelines
Note: This is for learning and local development. For production websites, use real VPS providers with proper infrastructure, backups, and security.