1

Let me try to explain what i want to achieve:

I am doing wordpress development and and try to set it up nicely via docker. I have a folder with themes and plugin where i my develop plugins and themes in. This folder also contains Docker files to be able to run them i a container where wordpress is installed via its docker image. So far so good, but i cannot really achieve what i want, which is keep my code separate from everything else while maintaining code changes being propagated to the container.

Using the official wordpress image (https://hub.docker.com/_/wordpress/) you should put preinstalled themes and plugins in the respective folders under /var/www/html/wp-content. Using a docker-compose.yml with volume directives i can achieve that.

web:
    image: wordpress
    ports: [...]
    environment:[...]
    volumes:
        - ./plugins:/var/www/html/wp-content/plugins/
        - ./themes:/var/www/html/wp-content/themes/

However in that case when the container is started, wordpress copies itself into the /var/www/html/ folder, also placing default themes and plugins in above folders. And my original structure is "compromised". I do not want that.

The problem is i do not find any way to keep my original plugins and themes structure unaffected while maintaining code changes being instantly propagated to the docker container. I could possibly mount plugin and themes one-by-one but that is also not what i want.

I tried to customize the Dockerfile or a custom entrypoint and fiddle around with symlinking etc. But actually i could not find a way to do that. Probably i am doing it all wrong and should not be doing that anyway. Any suggestions?

patman
  • 2,780
  • 4
  • 30
  • 54
  • I assume by 'affected' you mean when you want to push only your changes to the git you also see other changes as well? If that's the case, why not just add a `.gitignore` with things you don't want to commit to the repo? You could look if bedrock setup has a docker image (https://roots.io/bedrock/). They have core separated from your plugin/theme. – dingo_d Dec 02 '20 at 12:48
  • Thanks i will take a look at the link provided. Well `.gitignore` is not exactly the same thing. As i only have a docker definition i do not exactly know the wordpress contents that will be installed. If wordpress i.e. publishes a new theme i would need to adjust `.gitingore`. Furthermore i could pretty easily install other 3rd party plugins via the docker image, and not having to list them in my gitignore all the time. After all even if gitignored they appear in the filesystem at the same place where i do not want them. – patman Dec 02 '20 at 14:00

1 Answers1

5

How the official wordpress docker image works...

Where the docker-compose.yml is located, is the current theme folder!

Attempting to map the themes folder using symlinks is not good idea, because this is where testing theme exists. This will just confuse things which i'm assuming you maybe experiencing from you question.

This is the common thing that I see a lot in noob docker wp devs. They think they have to edit files which docker has built. You never edit or access container files built by docker... because when you docker-compose down, all that data will be deleted.

By default...

Every time you run docker-compose up -d on the folder containing your docker-compose.yml config file, docker will build a brand new wordpress installation every time.

Then when you run docker-compose down it will remove all the containers, deleting database, plugins and uploads data.

So next time when you docker-compose up -d on this project, you will have to re-go through the wordpress install setup, and you will have lost all of your previous development data, plugins and uploads.

Local docker wp handling...

Treat every local wordpress project as its own!

I'm not including child theme usages in this answer, but my methods can be extended to accommodate child themes.

When I say treat each project as it's own. The project folder containing the docker-compose.yml is your git repository! As time goes by you will have a folder for each individual local wp project/site on your OS.

Each of these local folders is the development theme for your current project/site!

Don't try and use a single docker-compose.yml config to handle multiple projects/themes until you understand how docker rebuilds containers based on your configs.

A brand new wordpress theme docker workflow...

Create a new empty local folder/project.

Add docker-compose.yml file to your project..

enter image description here

Copy this code below into your yml config...

version: '3.7'

networks:
  wordpress:
    ipam:
      config:
        - subnet: 172.25.0.0/16

services:

  # here is our mysql database
  db:
    image: mysql:5.7
    volumes:
      - ./db:/var/lib/mysql:delegated
    ports:
      - "3306:3306"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: somewordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
    networks:
      - wordpress

  # here is our wordpress server
  wordpress:
    depends_on:
      - db
    image: wordpress:latest
    volumes:
      # our persistent local data re routing
      - .:/var/www/html/wp-content/themes/testing:delegated
      - ./plugins:/var/www/html/wp-content/plugins
      - ./uploads:/var/www/html/wp-content/uploads
      - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
    ports:
      - "80:80"
    restart: always
    networks:
      - wordpress
    environment:
      # our local dev environment
      WORDPRESS_DEBUG: 1
      DEVELOPMENT: 1
      # docker wp config settings
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_AUTH_KEY: 5f6ede1b94d25a2294e29eeba929a8c80a5ac0fb
      WORDPRESS_SECURE_KEY: 5f6ede1b94d25a2294e29eeba929a8c80a5ac0fb
      WORDPRESS_LOGGED_IN_KEY: 5f6ede1b94d25a2294e29eeba929a8c80a5ac0fb
      WORDPRESS_NONCE_KEY: 5f6ede1b94d25a2294e29eeba929a8c80a5ac0fb
      WORDPRESS_SECURE_AUTH_SALT: 5f6ede1b94d25a2294e29eeba929a8c80a5ac0fb
      WORDPRESS_LOGGED_IN_SALT: 5f6ede1b94d25a2294e29eeba929a8c80a5ac0fb
      WORDPRESS_NONCE_SALT: 5f6ede1b94d25a2294e29eeba929a8c80a5ac0fb
      WORDPRESS_CONFIG_EXTRA: |

        /* Development parameters */
        define('WP_CACHE', false);
        define('ENVIRONMENT', 'local');
        define('WP_DEBUG', true);

        /* Configure mail server */
        define('WORDPRESS_SMTP_AUTH', false);
        define('WORDPRESS_SMTP_SECURE', '');
        define('WORDPRESS_SMTP_HOST', 'mailhog');
        define('WORDPRESS_SMTP_PORT', '1025');
        define('WORDPRESS_SMTP_USERNAME', null);
        define('WORDPRESS_SMTP_PASSWORD', null);
        define('WORDPRESS_SMTP_FROM', 'whoever@example.com');
        define('WORDPRESS_SMTP_FROM_NAME', 'Whoever');

        /* add any more custom wp-config defines here */

  # here is our mail hog server
  mailhog:
    image: mailhog/mailhog:latest
    ports:
      - "8025:8025"
    networks:
      - wordpress

Make sure your docker app is up to date. If any previous docker project is running, use this command in project to shut it down...

docker-compose down

Then in your new project folder run this command to build your new wordpress site...

docker-compose up -d

Now this will build a new installation with persistent folders in your project folder...

enter image description here

Before you access the site you need to update the uploads.ini folder to an actual uploads.ini file with this config code...

file_uploads = On
memory_limit = 2000M
upload_max_filesize = 2000M
post_max_size = 2000M
max_execution_time = 600

So now you will have...

enter image description here

Before you access the site again it's probably a good idea to put some basic theme files in so your theme will run when you visit the site and admin.

enter image description here

For mailhog to work and receive outgoing mail from your local wordpress environment you will need to add this to your functions.php...

// add the action
add_action('wp_mail_failed', 'action_wp_mail_failed', 10, 1);

// configure PHPMailer to send through SMTP
add_action('phpmailer_init', function ($phpmailer) {

    $phpmailer->isSMTP();
    // host details
    $phpmailer->SMTPAuth = WORDPRESS_SMTP_AUTH;
    $phpmailer->SMTPSecure = WORDPRESS_SMTP_SECURE;
    $phpmailer->SMTPAutoTLS = false;
    $phpmailer->Host = WORDPRESS_SMTP_HOST;
    $phpmailer->Port = WORDPRESS_SMTP_PORT;
    // from details
    $phpmailer->From = WORDPRESS_SMTP_FROM;
    $phpmailer->FromName = WORDPRESS_SMTP_FROM_NAME;
    // login details
    $phpmailer->Username = WORDPRESS_SMTP_USERNAME;
    $phpmailer->Password = WORDPRESS_SMTP_PASSWORD;

});

Now these urls below will work...

So every time you docker-compose down and docker-compose up -d on this project, your environment will boot up exactly where you left off.

As your project grows, advanced theme development, keeping every single thing consolidated in the project/folder makes managing multiple theme project easier.

Like this for example...

enter image description here

Deploying an existing wp theme locally with docker workflow...

If you want to deploy an existing theme/project locally. Get your existing theme folder and contents, and locate it locally on your OS.

Do not try and map the themes folder in your docker-compose.yml so you can manually add themes and activate it. This is not the way!

Essentially (same as above), create a docker-compose.yml file inside the root of your existing theme (locally).

If you don't require any existing db/plugins/uploads for this theme to run, then simply (as shown above) use docker-compose up -d to build brand new wordpress installation with persistent db/plugins/uploads mapped into this existing local theme folder.

Obviously if your existing theme project folder previously contains any child folders named db/plugins/uploads, then attempting to rebuild using my yml config (mentioned above) will probably go tits up. Make sure your existing theme folder has no child folders with these names... db/plugins/uploads.

If you want to deploy an existing theme/project using docker whilst retaining db/plugins/uploads data from a production or staging environment then...

Create a folder and sub folder called docker/db-dumps in your existing theme along with a docker-compose.yml file with yml configs (as shown above).

Before dockering up, place a mySql db dump file (example dump.sql) in docker/db-dumps and any required plugins/uploads in plugins and uploads folders in this existing theme. Like this...

enter image description here

Please note in the above screenshot demo I am not displaying any existing theme files. If you are attempting this, the above demo screenshot child directory structure would be additional to your existing theme folder files.

With your production or staging database sql dump and plugins/uploads located correctly, the only thing you need to edit in your docker-compose.yml is this...

  # here is our mysql database
  db:
    image: mysql:5.7
    volumes:
#      - ./db:/var/lib/mysql:delegated (remove this and add the line below)
      - ./docker/db-dumps:/docker-entrypoint-initdb.d:delegated

This means when you docker-compose up -d, it will run the dump initially and generate the dumped sql into a new installation, deploying the database to the db folder.

Meaning if you wish to rebuild this data in your next docker-compose up -d, then you need to revert the docker-compose.yml back to my original db volume so docker rebuilds from where you left off.

Git ignore example...

As shown in my advanced dev screenshot, you will see a git ignore file. Generally this is what the git ignore file contents looks like if you are using the tech shown in my advanced dev screenshot...

/vendor
/uploads
/db
/dist
/node_modules
/plugins
/docker
joshmoto
  • 4,472
  • 1
  • 26
  • 45
  • Hi @joshmoto, thank you very much for that detailed answer. To me a lot of it was clear, but it might help a lot of people. Still i disagree on some parts and i think this setup comes with some drawbacks: a) i do not agree that the wordpress docker image is enforcing the theme to live in `.`, this is only done via volumes directive. b) This brings the problem, that when you fire up the container and open the cli, `ls wp-content/themes/testing/` you will see the complete `plugins` folder in there (may not cause problems, but does not belong there) – patman Dec 03 '20 at 14:43
  • 1
    c) i maybe was not clear enough, but the point was not theme or plugin development but rather being able to spawn a wordpress instance with several themes and plugins to test around from different sources, which your answer does not solve. To be clear, i might of think of it like a clients wordpress page, where some plugins and themes are installed (from their own repositories), and when they tell you they might want to change the theme you could fire up docker with the new theme to test it out, once it works include in the docker-config (via fetch from a repository or something) – patman Dec 03 '20 at 14:48
  • But to conclude, still thank you very much. The answer is still helpful and exceptionally detailed. I might accept it anyway. – patman Dec 03 '20 at 14:49
  • If it’s not what you are after you don’t have to accept it. I have like 30 local docker projects now, each one is an individual folder, and backed up as repository, literally so easy to docker up and down on these, and retain all the existing data from previous session. WordPress local dev has never been easier. Makes deployment to staging and production easier this way because the deployment mapping is saved per project too. – joshmoto Dec 03 '20 at 18:39
  • patman I've the same concern, thx for the question :) (not only for WordPress, it's a general CMS concern ... thx @joshmoto for your answer, very interesting. joshmoto : next question, how do you deal the installation of plugins between env, and how do you deal content (posts, pages, ...) between different env. Let's say my customer create blog posts in staging, I would like to get them back in local. Let's say I install a plugin, I would like to get it also on staging. Any suggestions ? Thx – Alberty Pascal Mar 05 '21 at 17:05
  • 1
    @AlbertyPascal I just zip the plugins folder and upload the zip folder staging via cpanel and extract the zip and make sure plugins are synced this way. To get staging database into your local, simply create full quick mysql dump, and place .sql file (on its own) in `docker/db-dumps` modify the `.yml` db volume line to... (follow comments in img) https://imgur.com/a/j1ZggDZ – joshmoto Mar 05 '21 at 18:29
  • Great post, I still had one minor improvement. Change the volumes to: volumes: - ./wp-content/uploads:/usr/src/wordpress/wp-content/uploads - ./wp-content/plugins:/usr/src/wordpress/wp-content/plugins - ./wp-content/themes:/usr/src/wordpress/wp-content/themes - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini See: https://stackoverflow.com/questions/68343519/docker-wordpress-cant-update-install-plugins/73493735#73493735 – Richard Scholtens Aug 25 '22 at 21:12
  • 1
    @RichardScholtens total your preference dude, I now map my local docker wp project folders a little differently to when i posted this answer. Rather than mapping everything into a `wp-content` I simply map wp-content sub directories individually so my project structure now looks like this... https://i.imgur.com/raskVFq.png – joshmoto Aug 28 '22 at 21:45
  • @joshmoto, I did an edit on the stackoverflow link I showed before. Found away to bypass the rights while adding new themes, plugins and media. What do you think of this construction? Any tips on how to make the work process more efficient? – Richard Scholtens Aug 30 '22 at 09:57