{"id":13494,"date":"2025-10-28T11:38:33","date_gmt":"2025-10-28T05:53:33","guid":{"rendered":"https:\/\/nestnepal.com\/blog\/?p=13494"},"modified":"2025-10-28T11:44:37","modified_gmt":"2025-10-28T05:59:37","slug":"migrate-php-mysql-website-into-docker-container","status":"publish","type":"post","link":"https:\/\/nestnepal.com\/blog\/migrate-php-mysql-website-into-docker-container\/","title":{"rendered":"Migrating a PHP\/MySQL Website into Docker Containers: A Step-by-Step Guide"},"content":{"rendered":"\n<p>If you&#8217;re still running that PHP website on a traditional LAMP stack setup, you&#8217;re not alone. Thousands of websites across Nepal and beyond are built this way, from small business sites in Biratnagar to complex web applications serving clients in Kathmandu. But as your business grows and deployment becomes more complex, you&#8217;ve probably started hearing about Docker and wondering if it&#8217;s time to make the leap.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"800\" height=\"445\" data-src=\"https:\/\/nestnepal.com\/blog\/wp-content\/uploads\/2025\/10\/image-1.png\" alt=\"docker-container\" class=\"wp-image-13496 lazyload\" data-srcset=\"https:\/\/nestnepal.com\/blog\/wp-content\/uploads\/2025\/10\/image-1.png 800w, https:\/\/nestnepal.com\/blog\/wp-content\/uploads\/2025\/10\/image-1-300x167.png 300w, https:\/\/nestnepal.com\/blog\/wp-content\/uploads\/2025\/10\/image-1-768x427.png 768w\" data-sizes=\"(max-width: 800px) 100vw, 800px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 800px; --smush-placeholder-aspect-ratio: 800\/445;\" \/><\/figure>\n\n\n\n<p>Here&#8217;s the thing: <a href=\"https:\/\/nestnepal.com\/blog\/wordpress-docker-compose-step-wise-guide-2025\/\">migrating to Docker<\/a> isn&#8217;t just about following trends. It&#8217;s about solving real problems that every developer faces like inconsistent environments, deployment headaches, scaling issues, and that dreaded &#8220;it works on my machine&#8221; syndrome. Today, we&#8217;re going to walk through exactly how to containerize your PHP\/MySQL application, step by step, with all the real-world gotchas I&#8217;ve learned from migrating dozens of sites.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Why Migrate to Docker Container? The Real Benefits<\/strong><\/h2>\n\n\n\n<p>Before we dive into the technical stuff, let&#8217;s talk about why this migration makes sense in 2025:<\/p>\n\n\n\n<p><strong>Development Consistency<\/strong>: No more &#8220;install Apache, PHP 8.1, MySQL 5.7, configure virtual hosts&#8230;&#8221; on every new developer&#8217;s machine. One docker-compose up and everyone&#8217;s running identical environments.<\/p>\n\n\n\n<p><strong>Easy Deployment<\/strong>: Deploy the same containers to staging and production. No more environment-specific bugs that only show up when you go live.<\/p>\n\n\n\n<p><strong>Version Management<\/strong>: Need to test PHP 8.2? Just change one line in your Dockerfile. Want to rollback? Easy.<\/p>\n\n\n\n<p><strong>Resource Efficiency<\/strong>: Containers <a href=\"https:\/\/en.wikipedia.org\/wiki\/Kernel_(operating_system)\" target=\"_blank\" rel=\"noopener\">share the host OS kernel<\/a>, using fewer resources than full virtual machines.<\/p>\n\n\n\n<p><strong>Scalability<\/strong>: When your business grows (and it will!), scaling individual services becomes much simpler.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Understanding Your Current Setup<\/strong><\/h2>\n\n\n\n<p>Most traditional PHP\/MySQL sites follow this pattern:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Component<\/strong><\/td><td><strong>Traditional Setup<\/strong><\/td><td><strong>Docker Equivalent<\/strong><\/td><\/tr><tr><td><strong>Web Server<\/strong><\/td><td>Apache\/Nginx on host<\/td><td>Nginx container<\/td><\/tr><tr><td><strong>PHP<\/strong><\/td><td>mod_php or PHP-FPM<\/td><td>PHP-FPM container<\/td><\/tr><tr><td><strong>Database<\/strong><\/td><td>MySQL\/MariaDB on host<\/td><td>MySQL container<\/td><\/tr><tr><td><strong>File Storage<\/strong><\/td><td>Direct filesystem<\/td><td>Docker volumes<\/td><\/tr><tr><td><strong>Configuration<\/strong><\/td><td>Multiple config files<\/td><td>Environment variables + configs<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>The key insight is that we&#8217;re not changing your application&#8217;s logic, we&#8217;re just packaging it differently.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Pre-Migration Assessment<\/strong><\/h2>\n\n\n\n<p>Before touching any code, let&#8217;s audit what you&#8217;re working with:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>1. Document Your Current Environment<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Check your current versions\nphp -v\nmysql --version\napache2 -v  # or nginx -v\n\n# List installed PHP extensions\nphp -m\n\n# Check database size and structure\nmysql -u root -p -e \"SELECT table_schema AS 'Database', \n    ROUND(SUM(data_length + index_length) \/ 1024 \/ 1024, 2) AS 'Size (MB)' \n    FROM information_schema.tables \n    GROUP BY table_schema;\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>2. Identify Dependencies<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>What PHP extensions does your app use?<\/li>\n\n\n\n<li>Are there any custom Apache\/Nginx configurations?<\/li>\n\n\n\n<li>Do you have any cron jobs running?<\/li>\n\n\n\n<li>Are there file upload directories that need persistence?<\/li>\n\n\n\n<li>Any external service connections that need special network setup?<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>3. Code Audit Checklist<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Hardcoded localhost database connections<\/li>\n\n\n\n<li>File paths that assume specific directory structures<\/li>\n\n\n\n<li>Sessions stored in files vs database<\/li>\n\n\n\n<li>Any absolute paths in your code<\/li>\n\n\n\n<li>Mail server configurations<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step-by-Step Migration Process<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 1: Project Structure Setup<\/strong><\/h3>\n\n\n\n<p>Let&#8217;s create a clean Docker structure for your existing project:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>your-website\/\n\u251c\u2500\u2500 docker-compose.yml\n\u251c\u2500\u2500 docker\/\n\u2502   \u251c\u2500\u2500 nginx\/\n\u2502   \u2502   \u251c\u2500\u2500 Dockerfile\n\u2502   \u2502   \u2514\u2500\u2500 default.conf\n\u2502   \u251c\u2500\u2500 php\/\n\u2502   \u2502   \u251c\u2500\u2500 Dockerfile\n\u2502   \u2502   \u2514\u2500\u2500 php.ini\n\u2502   \u2514\u2500\u2500 mysql\/\n\u2502       \u2514\u2500\u2500 init\/\n\u2502           \u2514\u2500\u2500 01-create-database.sql\n\u251c\u2500\u2500 src\/  (your existing PHP files go here)\n\u251c\u2500\u2500 data\/  (for persistent MySQL data)\n\u2514\u2500\u2500 logs\/  (for application logs)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 2: Creating the Docker Compose File<\/strong><\/h3>\n\n\n\n<p>This is the heart of your containerized setup:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>version: '3.8'\n\nservices:\n  nginx:\n    build:\n      context: .\/docker\/nginx\n      dockerfile: Dockerfile\n    container_name: ${PROJECT_NAME}-nginx\n    ports:\n      - \"${NGINX_PORT:-80}:80\"\n      - \"${NGINX_SSL_PORT:-443}:443\"\n    volumes:\n      - .\/src:\/var\/www\/html\n      - .\/logs\/nginx:\/var\/log\/nginx\n    depends_on:\n      - php\n    networks:\n      - app-network\n    restart: unless-stopped\n\n  php:\n    build:\n      context: .\/docker\/php\n      dockerfile: Dockerfile\n    container_name: ${PROJECT_NAME}-php\n    volumes:\n      - .\/src:\/var\/www\/html\n      - .\/docker\/php\/php.ini:\/usr\/local\/etc\/php\/php.ini\n      - .\/logs\/php:\/var\/log\/php\n    environment:\n      - DB_HOST=mysql\n      - DB_PORT=3306\n      - DB_NAME=${DB_NAME:-your_database}\n      - DB_USER=${DB_USER:-your_user}\n      - DB_PASS=${DB_PASS:-your_password}\n    depends_on:\n      mysql:\n        condition: service_healthy\n    networks:\n      - app-network\n    restart: unless-stopped\n\n  mysql:\n    image: mysql:8.0\n    container_name: ${PROJECT_NAME}-mysql\n    ports:\n      - \"${MYSQL_PORT:-3306}:3306\"\n    environment:\n      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS:-rootpassword}\n      MYSQL_DATABASE: ${DB_NAME:-your_database}\n      MYSQL_USER: ${DB_USER:-your_user}\n      MYSQL_PASSWORD: ${DB_PASS:-your_password}\n    volumes:\n      - .\/data\/mysql:\/var\/lib\/mysql\n      - .\/docker\/mysql\/init:\/docker-entrypoint-initdb.d\n      - .\/logs\/mysql:\/var\/log\/mysql\n    healthcheck:\n      test: &#91;\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\"]\n      timeout: 20s\n      retries: 10\n    networks:\n      - app-network\n    restart: unless-stopped\n\n  phpmyadmin:\n    image: phpmyadmin\/phpmyadmin\n    container_name: ${PROJECT_NAME}-phpmyadmin\n    ports:\n      - \"${PMA_PORT:-8080}:80\"\n    environment:\n      PMA_HOST: mysql\n      PMA_PORT: 3306\n      PMA_USER: ${DB_USER:-your_user}\n      PMA_PASSWORD: ${DB_PASS:-your_password}\n    depends_on:\n      - mysql\n    networks:\n      - app-network\n    restart: unless-stopped\n\nvolumes:\n  mysql_data:\n\nnetworks:\n  app-network:\n    driver: bridge<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 3: PHP Container Configuration<\/strong><\/h3>\n\n\n\n<p>Create docker\/php\/Dockerfile:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>FROM php:8.1-fpm\n\n# Install system dependencies\nRUN apt-get update &amp;&amp; apt-get install -y \\\n    git \\\n    curl \\\n    libpng-dev \\\n    libonig-dev \\\n    libxml2-dev \\\n    libzip-dev \\\n    zip \\\n    unzip \\\n    &amp;&amp; docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip\n\n# Install Composer\nCOPY --from=composer:latest \/usr\/bin\/composer \/usr\/bin\/composer\n\n# Set working directory\nWORKDIR \/var\/www\/html\n\n# Copy custom PHP configuration\nCOPY php.ini \/usr\/local\/etc\/php\/\n\n# Create user for running the application\nRUN groupadd -g 1000 www &amp;&amp; \\\n    useradd -u 1000 -ms \/bin\/bash -g www www\n\n# Change ownership of working directory\nRUN chown -R www:www \/var\/www\/html\n\n# Switch to user www\nUSER www\n\n# Expose port 9000 and start php-fpm server\nEXPOSE 9000\nCMD &#91;\"php-fpm\"]<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 4: Nginx Configuration<\/strong><\/h3>\n\n\n\n<p>Create docker\/nginx\/Dockerfile:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>FROM nginx:alpine\n\n# Copy custom nginx config\nCOPY default.conf \/etc\/nginx\/conf.d\/\n\n# Expose port 80\nEXPOSE 80\n\nCMD &#91;\"nginx\", \"-g\", \"daemon off;\"]<\/code><\/pre>\n\n\n\n<p>And docker\/nginx\/default.conf:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>server {\n    listen 80;\n    server_name localhost;\n    root \/var\/www\/html;\n    index index.php index.html index.htm;\n\n    # Security headers\n    add_header X-Frame-Options \"SAMEORIGIN\" always;\n    add_header X-XSS-Protection \"1; mode=block\" always;\n    add_header X-Content-Type-Options \"nosniff\" always;\n\n    # Handle static files\n    location ~* \\.(js|css|png|jpg|jpeg|gif|ico|svg)$ {\n        expires 1y;\n        add_header Cache-Control \"public, immutable\";\n        try_files $uri =404;\n    }\n\n    # Main location block\n    location \/ {\n        try_files $uri $uri\/ \/index.php?$query_string;\n    }\n\n    # PHP files processing\n    location ~ \\.php$ {\n        fastcgi_pass php:9000;\n        fastcgi_index index.php;\n        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n        include fastcgi_params;\n        \n        # Security: Don't execute PHP files in uploads directory\n        location ~* \/uploads\/.*\\.php$ {\n            return 403;\n        }\n    }\n\n    # Security: Deny access to sensitive files\n    location ~ \/\\. {\n        deny all;\n        return 404;\n    }\n\n    location ~ \/(README|CHANGELOG|composer\\.(json|lock))$ {\n        deny all;\n        return 404;\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 5: Environment Configuration<\/strong><\/h3>\n\n\n\n<p>Create a .env file in your project root:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PROJECT_NAME=your-website\nNGINX_PORT=80\nNGINX_SSL_PORT=443\nMYSQL_PORT=3306\nPMA_PORT=8080\n\nDB_HOST=mysql\nDB_NAME=your_database_name\nDB_USER=your_db_user\nDB_PASS=your_secure_password\nDB_ROOT_PASS=your_root_password<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Updating Your PHP Application<\/strong><\/h2>\n\n\n\n<p>Now comes the important part, updating your existing PHP code to work with containers:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Database Connection Updates<\/strong><\/h3>\n\n\n\n<p><strong>Before (traditional):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n$host = 'localhost';\n$dbname = 'your_database';\n$username = 'your_user';\n$password = 'your_password';\n\ntry {\n    $pdo = new PDO(\"mysql:host=$host;dbname=$dbname\", $username, $password);\n} catch(PDOException $e) {\n    die('Connection failed: ' . $e-&gt;getMessage());\n}\n?&gt;<\/code><\/pre>\n\n\n\n<p><strong>After (Docker):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n$host = $_ENV&#91;'DB_HOST'] ?? 'mysql';\n$dbname = $_ENV&#91;'DB_NAME'] ?? 'your_database';\n$username = $_ENV&#91;'DB_USER'] ?? 'your_user';\n$password = $_ENV&#91;'DB_PASS'] ?? 'your_password';\n\ntry {\n    $pdo = new PDO(\"mysql:host=$host;dbname=$dbname\", $username, $password);\n    $pdo-&gt;setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n} catch(PDOException $e) {\n    error_log('Database connection failed: ' . $e-&gt;getMessage());\n    die('Database connection failed');\n}\n?&gt;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>File Upload Handling<\/strong><\/h3>\n\n\n\n<p>Make sure your upload directories are properly mapped:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\/\/ Use relative paths or environment variables\n$upload_dir = $_ENV&#91;'UPLOAD_DIR'] ?? '.\/uploads\/';\n\n\/\/ Ensure directory exists\nif (!is_dir($upload_dir)) {\n    mkdir($upload_dir, 0755, true);\n}\n\n\/\/ Your upload logic here\n?&gt;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Data Migration Strategy<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Method 1: Direct Database Import<\/strong><\/h3>\n\n\n\n<p>For smaller databases (under 1GB):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Export from your current server\nmysqldump -u root -p your_database &gt; backup.sql\n\n# Start your Docker services\ndocker-compose up -d\n\n# Import into containerized MySQL\ndocker-compose exec mysql mysql -u root -p your_database &lt; backup.sql<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Method 2: Live Migration for Large Databases<\/strong><\/h3>\n\n\n\n<p>For bigger databases or minimal downtime:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Set up replication<\/strong> from your current MySQL to the containerized one<\/li>\n\n\n\n<li><strong>Test thoroughly<\/strong> with the replica<\/li>\n\n\n\n<li><strong>Switch DNS\/traffic<\/strong> when ready<\/li>\n\n\n\n<li><strong>Stop replication<\/strong> and update configurations<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>File Migration<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Copy your existing website files to the src directory\ncp -r \/var\/www\/html\/* .\/src\/\n\n# Fix permissions\nsudo chown -R $USER:$USER .\/src\nchmod -R 755 .\/src<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Testing Your Containerized Application<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Start Your Services<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Build and start all services\ndocker-compose up -d --build\n\n# Check service status\ndocker-compose ps\n\n# View logs\ndocker-compose logs -f php\ndocker-compose logs -f nginx\ndocker-compose logs -f mysql<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Validation Checklist<\/strong><\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Test Case<\/strong><\/td><td><strong>Command\/Action<\/strong><\/td><td><strong>Expected Result<\/strong><\/td><\/tr><tr><td><strong>Web Access<\/strong><\/td><td>Visit http:\/\/localhost<\/td><td>Site loads correctly<\/td><\/tr><tr><td><strong>Database Connection<\/strong><\/td><td>Check app functionality<\/td><td>No DB errors<\/td><\/tr><tr><td><strong>PHP Info<\/strong><\/td><td>Create phpinfo.php page<\/td><td>PHP version and extensions correct<\/td><\/tr><tr><td><strong>File Uploads<\/strong><\/td><td>Test upload functionality<\/td><td>Files saved correctly<\/td><\/tr><tr><td><strong>Session Handling<\/strong><\/td><td>Login\/logout functionality<\/td><td>Sessions work properly<\/td><\/tr><tr><td><strong>Error Handling<\/strong><\/td><td>Check logs directory<\/td><td>Errors logged appropriately<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Common Migration Issues and Solutions<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Issue 1: Permission Problems<\/strong><\/h3>\n\n\n\n<p><strong>Symptom<\/strong>: &#8220;Permission denied&#8221; errors for file operations <\/p>\n\n\n\n<p><strong>Solution<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Fix container user permissions\ndocker-compose exec php chown -R www:www \/var\/www\/html\ndocker-compose exec nginx chown -R nginx:nginx \/var\/log\/nginx<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Issue 2: Database Connection Timeouts<\/strong><\/h3>\n\n\n\n<p><strong>Symptom<\/strong>: &#8220;Connection timeout&#8221; or &#8220;Can&#8217;t connect to MySQL&#8221; <\/p>\n\n\n\n<p><strong>Solution<\/strong>: Add health checks and proper service dependencies (already included in our docker-compose.yml)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Issue 3: PHP Extensions Missing<\/strong><\/h3>\n\n\n\n<p><strong>Symptom<\/strong>: &#8220;Call to undefined function&#8221; errors <\/p>\n\n\n\n<p><strong>Solution<\/strong>: Add missing extensions to your PHP Dockerfile:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>RUN docker-php-ext-install mysqli pdo_mysql gd zip curl xml<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Issue 4: Session Storage Issues<\/strong><\/h3>\n\n\n\n<p><strong>Symptom<\/strong>: Users getting logged out randomly&nbsp;<\/p>\n\n\n\n<p><strong>Solution<\/strong>: Use database sessions or shared session storage:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ini_set('session.save_handler', 'files');\nini_set('session.save_path', '\/tmp\/sessions');<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Performance Optimization<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>PHP-FPM Tuning<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Update your docker\/php\/php.ini:\n; Performance settings\nmemory_limit = 256M\nmax_execution_time = 300\nmax_input_time = 300\npost_max_size = 64M\nupload_max_filesize = 32M\n\n; OPcache settings\nopcache.enable = 1\nopcache.memory_consumption = 128\nopcache.max_accelerated_files = 4000\nopcache.validate_timestamps = 0\nopcache.revalidate_freq = 0<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>MySQL Configuration<\/strong><\/h3>\n\n\n\n<p>Add to your docker-compose.yml MySQL service:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mysql:\n  # ... other configuration\n  command: &gt;\n    --innodb-buffer-pool-size=256M\n    --innodb-log-file-size=64M\n    --max-connections=100\n    --query-cache-type=1\n    --query-cache-size=32M<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Nginx Optimization<\/strong><\/h3>\n\n\n\n<p>Update your nginx configuration:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Enable gzip compression\ngzip on;\ngzip_vary on;\ngzip_min_length 1024;\ngzip_types text\/plain text\/css text\/xml text\/javascript application\/javascript application\/xml+rss application\/json;\n\n# Enable HTTP\/2\nlisten 443 ssl http2;\n\n# Connection keep-alive\nkeepalive_timeout 30;\nkeepalive_requests 100;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Monitoring and Maintenance<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Health Monitoring Script<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Create scripts\/health-check.sh:\n#!\/bin\/bash\n\n# Check if all services are running\nservices=(\"nginx\" \"php\" \"mysql\")\n\nfor service in \"${services&#91;@]}\"; do\n    if docker-compose ps $service | grep -q \"Up\"; then\n        echo \"\u2713 $service is running\"\n    else\n        echo \"\u2717 $service is down\"\n        docker-compose logs $service\n    fi\ndone\n\n# Check disk usage\necho \"Docker disk usage:\"\ndocker system df\n\n# Check database connections\necho \"MySQL connections:\"\ndocker-compose exec mysql mysql -u root -p${DB_ROOT_PASS} -e \"SHOW PROCESSLIST;\"<\/code><\/pre>\n\n\n\n<p><strong>Backup Strategy<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n# backup.sh\n\nDATE=$(date +%Y%m%d_%H%M%S)\nBACKUP_DIR=\".\/backups\/$DATE\"\n\nmkdir -p $BACKUP_DIR\n\n# Backup database\ndocker-compose exec mysql mysqldump -u root -p${DB_ROOT_PASS} ${DB_NAME} &gt; $BACKUP_DIR\/database.sql\n\n# Backup application files\ntar -czf $BACKUP_DIR\/files.tar.gz .\/src\n\n# Keep only last 7 days of backups\nfind .\/backups -name \"20*\" -type d -mtime +7 -exec rm -rf {} \\;\n\necho \"Backup completed: $BACKUP_DIR\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Deployment to Production<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Using Docker Compose in Production<\/strong><\/h3>\n\n\n\n<p>Update your production docker-compose.yml:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Add to your services\nservices:\n  nginx:\n    # ... existing config\n    restart: always\n    logging:\n      driver: \"json-file\"\n      options:\n        max-size: \"10m\"\n        max-file: \"3\"\n  \n  php:\n    # ... existing config  \n    restart: always\n    environment:\n      - APP_ENV=production\n    logging:\n      driver: \"json-file\"\n      options:\n        max-size: \"10m\"\n        max-file: \"3\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Security Hardening<\/strong><\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Use specific image tags<\/strong> instead of latest<\/li>\n\n\n\n<li><strong>Run containers as non-root users<\/strong> (already configured)<\/li>\n\n\n\n<li><strong>Limit container resources<\/strong>:<br>deploy:&nbsp; resources:&nbsp; &nbsp; limits:&nbsp; &nbsp; &nbsp; memory: 512M&nbsp; &nbsp; &nbsp; cpus: 0.5<\/li>\n\n\n\n<li><strong>Use secrets for sensitive data<\/strong> instead of environment variables<\/li>\n\n\n\n<li><strong>Regular security updates<\/strong> for base images<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Scaling Considerations<\/strong><\/h2>\n\n\n\n<p>When your containerized application needs to handle more traffic:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Horizontal Scaling<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Scale PHP workers\ndocker-compose up -d --scale php=3\n\n# Use a load balancer (nginx upstream)\nupstream php_backend {\n    server php_1:9000;\n    server php_2:9000;\n    server php_3:9000;\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Database Scaling<\/strong><\/h3>\n\n\n\n<p>Consider read replicas or database clustering:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mysql-master:\n  image: mysql:8.0\n  environment:\n    - MYSQL_REPLICATION_MODE=master\n    \nmysql-slave:\n  image: mysql:8.0\n  environment:\n    - MYSQL_REPLICATION_MODE=slave\n    - MYSQL_MASTER_HOST=mysql-master<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h2>\n\n\n\n<p>Migrating your PHP\/MySQL website to Docker containers might seem daunting at first, but the benefits are worth it. You get consistent environments, easier deployments, better resource utilization, and a foundation that scales with your business.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img decoding=\"async\" width=\"630\" height=\"473\" data-src=\"https:\/\/nestnepal.com\/blog\/wp-content\/uploads\/2025\/10\/image-2.png\" alt=\"container\" class=\"wp-image-13497 lazyload\" style=\"--smush-placeholder-width: 630px; --smush-placeholder-aspect-ratio: 630\/473;width:708px;height:auto\" data-srcset=\"https:\/\/nestnepal.com\/blog\/wp-content\/uploads\/2025\/10\/image-2.png 630w, https:\/\/nestnepal.com\/blog\/wp-content\/uploads\/2025\/10\/image-2-300x225.png 300w, https:\/\/nestnepal.com\/blog\/wp-content\/uploads\/2025\/10\/image-2-400x300.png 400w\" data-sizes=\"(max-width: 630px) 100vw, 630px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" \/><\/figure>\n\n\n\n<p>The key is to take it step by step. Don&#8217;t try to migrate everything at once, start with a development environment, test thoroughly, then gradually move to staging and production. Document everything you do, because you&#8217;ll likely be helping other developers in Nepal make the same transition.<\/p>\n\n\n\n<p>Remember that containerization is not just a one-time migration, it&#8217;s a new way of thinking about application deployment and management. The initial investment in learning and setup pays dividends in reduced deployment stress, faster development cycles, and more reliable applications.<\/p>\n\n\n\n<p>Whether you&#8217;re running a small business website in Pokhara or managing enterprise applications in Kathmandu, Docker gives you the tools to deploy confidently and scale effectively. The future of web development is containerized, and now you&#8217;re ready to be part of it.<\/p>\n\n\n\n<p>At <a href=\"https:\/\/nestnepal.com\/\">Nest Nepal<\/a>, we&#8217;ve helped dozens of businesses make this transition successfully. The combination of proper planning, thorough testing, and gradual migration ensures that your website keeps running smoothly while you gain all the benefits of modern containerized deployment.<\/p>\n\n\n\n<p>Ready to make the leap? Start with a development environment, follow this guide step by step, and don&#8217;t hesitate to reach out when you need help. Your future self will thank you for making the move to Docker! \ud83d\udc33<\/p>\n\n\n\n<p><em>Need assistance migrating your PHP\/MySQL application to Docker? Our experienced team at Nest Nepal can help you plan, execute, and optimise your containerization strategy. Contact us to learn more about our migration services.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you&#8217;re still running that PHP website on a traditional LAMP stack setup, you&#8217;re not alone. Thousands of websites across&#8230;<\/p>\n","protected":false},"author":15,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[438,112],"tags":[337],"class_list":["post-13494","post","type-post","status-publish","format-standard","hentry","category-business-tech","category-wordpress-hosting","tag-wordpress-website-security"],"_links":{"self":[{"href":"https:\/\/nestnepal.com\/blog\/wp-json\/wp\/v2\/posts\/13494","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nestnepal.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nestnepal.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nestnepal.com\/blog\/wp-json\/wp\/v2\/users\/15"}],"replies":[{"embeddable":true,"href":"https:\/\/nestnepal.com\/blog\/wp-json\/wp\/v2\/comments?post=13494"}],"version-history":[{"count":3,"href":"https:\/\/nestnepal.com\/blog\/wp-json\/wp\/v2\/posts\/13494\/revisions"}],"predecessor-version":[{"id":13534,"href":"https:\/\/nestnepal.com\/blog\/wp-json\/wp\/v2\/posts\/13494\/revisions\/13534"}],"wp:attachment":[{"href":"https:\/\/nestnepal.com\/blog\/wp-json\/wp\/v2\/media?parent=13494"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nestnepal.com\/blog\/wp-json\/wp\/v2\/categories?post=13494"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nestnepal.com\/blog\/wp-json\/wp\/v2\/tags?post=13494"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}