Scripts for a WordPress Weblog Farm [en]

A first step to WordPress-farming: a shell script and a PHP script which allow you to easily install a whole lot of WordPress weblogs in only a few minutes (I installed over 30 in less than 5 minutes). Scripts require adapting to your environment, of course.

Update 03.11.06: Batiste made me realise I should point the many people landing here in the search of multi-user WordPress to WordPress MU. All that I describe in this post is very pretty, but nowadays completely obsolete.

Here is the best solution I’ve managed to come up with in half a day to finally install over 30 WordPress weblogs in under 5 minutes (once the preparation work was done).

A shell script copies the image of a WordPress install to multiple directories and installs them. A PHP script then changes a certain number of options and settings in each weblog. It can be used later to run as a “patch” on all installed weblog if a setting needs modifying globally.

Here are the details of what I did.

I first downloaded and unzipped WordPress into a directory.

tar -xzvf latest.tar.gz
mv wordpress wp-farm-image

I cleaned up the install (removing wp-comments-popup.php and the import*.php files, for example), added a language directory (as I’m wp-farming in French) and modified index.php to my liking; in particular, I edited the import statement for the stylesheet so that it looked like this:

@import url( );

The styles directory is a directory in which I place a bunch of WordPress styles. I don’t need the style switcher capability, but I do need to styles. Later, users will be able to change styles simply by editing that line in their index.php (or I can do it for them).

Another very important thing I did was rename wp-config-sample.php to config-sample and fill in the database and language information. I replaced wp_ by xxx_ so that I had $table_prefix = 'xxx_';.

To make it easier to install plugins for everyone, correct the language files, and edit whatever may be in wp-images, I moved these three directories out of the image install and replaced them with symbolic links, taking inspiration from Shelley’s method for installing multiple WordPress weblogs.

mv image/wp-content common
mv image/wp-images common
mv image/wp-includes/languages common
ln -s common/wp-content image/wp-content
ln -s common/wp-images image/wp-images
ln -s common/languages image/wp-includes/languages

I also added an .htaccess file (after some painful tweaking on a test install).

Once my image was ready, I compiled a list of all the users I had to open weblogs for (one username per line) in a file named names.txt, which I placed in the root directory all the weblog subfolders were going to go in.

I then ran this shell script (many thanks to all those of you who helped me with it — you saved my life):

for x in `cat names.txt`
cp -rv /home/edublogs/wp-farm/image/ $x
cat $x/wp-config.php | sed "s/xxx/${x}/" > config.tmp
mv config.tmp $x/wp-config.php

This assumes that my WordPress install image was located in /home/edublogs/wp-farm/image/ and that the weblog addresses were of the form

This script copies the image to a directory named after the user, edits wp-config to set the table prefix to the username, and then successively wgets the install URLs to save me from loading them all in my browser.

After this step, I had a bunch of installed but badly configured weblogs (amongst other things, as I short-circuited the form before the third install step, they all think their siteurl is

Entered the PHP patch which tweaks settings directly in the database. I spent some time with a test install and PHPMyAdmin to figure out which fields I wanted to change and which values I wanted to give them, but overall it wasn’t too complicated to do. You’ll certainly need to heavily edit this file before using it if you try and duplicate what I did, but the basic structure and queries should remain the same.

I edited the user list at the top of the file, loaded it in my browser, and within less than a few seconds all my weblogs were correctly configured. I’ll use modified versions of this script later on when I need to change settings for all the weblogs in one go (for example, if I want to quickly install a plugin for everyone).

In summary:

  1. compile list of users
  2. prepare image install
  3. run shell script
  4. run PHP script

If you try to do this, I suggest you start by putting only two users in your user list, and checking thoroughly that everything installs and works correctly before doing it for 30 users. I had to tweak the PHP script quite a bit until I had all my settings correctly configured.

Hope this can be useful to some!

Update 29.09.2005: WARNING! Hacking WordPress installs to build a farm like this one is neat, but it gets much less neat when your weblog farm is spammed with animal porn comments. You then realise (oh, horror!) that none of the anti-spam plugins work on your beautiful construction, so you weed them out by hand as you can, armed with many a MySQL query. And then the journalist steps in — because, frankly, “sex with dogs” on a school website is just too good to be true. And then you can spend half a day writing an angry reaction to the shitty badly-researched article.

My apologies for the bad language. Think of how you’re going to deal with spam beforehand when you’re setting up a school blog project.

Requirements for a WordPress Installer Script [en]

To install 30+ WordPress blogs on a server, it would be nice to have an automatic installation script. Here is a list of what this script should do for me.

[fr] Pour installer plus de trente weblogs WordPress sur mon serveur, il serait utile d'avoir un script d'installation en PHP. Quelqu'un a offert d'en écrire un pour moi. Ce billet récapitule ce que devrait faire un tel script, de mon point de vue (installation et configuration de WordPress en fonction d'un nom d'utilisateur).

As you may know, I’m shortly going to install 30+ WordPress blogs on my server. Noderat on #wordpress kindly offered to have a go at writing a PHP script to automate WP installs. I sent him this list of what the ideal script should be able to do for me, but on second thoughts, I’m posting it here so that everybody may see it. Of course, if you know of an existing script which already does this, let me know!

  • take $username + $password as input
  • install wordpress in a subdir named “$username”, using table prefix “$username_” and with an extra user (on top of admin) named “$username” (password=”$password”), user level 3
  • mysql user should be “$username” too (password “$password” also), with grants only on the tables belonging to this weblog
  • set permalink scheme to
    /archives/%year%/%monthnum%/%day%/%postname%/ for monthlies and /categories for categories
  • generate .htaccess in directory, based on this template, with “blog” replaced by “$username” everywhere
  • in wp-config: define (‘WPLANG’, ‘fr’) and edit appropriate lines
  • wp-includes/languages and wp-content/plugins should be symlinked to directories I can specify in the script
  • blog admin password should be reset to something I can specify in the script

I should be able to edit the script config file to provide mysql root user + pass, wordpress database name.

It would also be interesting if the script was built in such a way that it could be further modified/developed to allow installation of blogs on separate subdomains rather than subfolders. From the point of view of the filesystem the blogs live in, this wouldn’t change much — but some extra WordPress options would need editing (e.g. blog address), as well as the Apache and Bind config files necessary to set up the subdomain. This is not mandatory, but could be useful at some point (if we’re thinking in the line of WordPress-farming).

Une matinée en Inde [en]

Une matinée tranquille en Inde, à  la maison, avec un portée de six petits chiots.

J’émerge vaguement de mon sommeil au moment où Sagar rentre à  la maison. Mon passage à  la position verticale me fait douloureusement savoir que le mal de tête qui me tient compagnie depuis plusieurs jours ne s’est pas fait la malle pendant la nuit.

Je dors dans le “salon”, la pièce qui accueille les gens qui entrent dans l’appartement, puisque la chambre à  coucher est occupée par les propriétaires, mes amis Shinde et Nisha, et surtout par une portée de six petits bergers allemands couinants et leur mère.

Etape incontournable au lever, l’opération-pipi prend ici une toute autre dimension. Les WC sont “à  la turque”, comme on dit par chez nous, ce qui ne me dérange nullement. Par contre, je ne peux pas dire que je sois réellement enthousiaste de partager les lieux d’aisance avec les trois chiens adultes qui vivent ici. Première étape, donc, rincer tout d’abord à  grande eau la cuvette et les alentours afin d’en éliminer l’urine de chien…

Je me re-pose sur mon lit et je lis quelques pages de mon fascinant livre sur les mélanges culturels dans la région d’Hyderabad au XVIIIe siècle. Shinde fait sa puja à  la cuisine pendant que Nisha prépare à  manger. Je croise Sagar brièvement en allant prendre quelques photos, puis il va se coucher.

Le long tintement continu de la cloche annonce la fin de la puja. Shinde passe dire bonjour, et quelques minutes après c’est Nisha qui m’appelle pour déjeuner.

Installée sur le seul tabouret de la cuisine, je finis de me réveiller en plongeant ma cuillère dans le délicieux upama épicé préparé par Nisha. Je prends cependant soin de laisser les piments sur le côté. Sagar, réveillé par les appels insistants de Shinde, vient chercher son assiette et disparaît.

Je suis la dernière à  finir. Shinde est parti au travail après une courte prière (ou invocation? — il faudra que je lui demande) devant son autel; Sagar dort déjà  à  poings fermés.

Nisha et moi parlons du programme de la journée: ce matin, je lirai, puis j’irai au café internet cet après-midi avant que nous sortions les trois (avec Sagar) manger chez Pizza Hut. Shinde ne sera pas de la partie, comme c’est le jour où il rend visite à  son guru.

Saisie d’une subite inspiration, je demande à  Nisha son meilleur couteau. Suivant mon conseil, Shinde a ramené hier soir un grand carton pour les chiots – malheureusement un peu petit. J’ai vite fait de le dépiauter un peu pour en faire une sorte d’enclos assez grand pour contenir mère et petits. (Suivant un de ces raisonnemetns dont le secret m’échappe, Shinde avait prévu de n’y mettre que les chiots.)

Justement, une des petites bêtes piaille plaintivement depuis quelques minutes. Nisha a fini vaisselle et nettoyage de cuisine, et est à  présent occupée à  la puja. Sagar, lui, ne s’est pas réveillé malgré le bruit (une faculté toute indienne). Je vais donc m’y coller, même si la mère a une fâcheuse tendance à  me considérer comme une menace pour ses petits et à  la jouer “fais gaffe ou je te mords.”

Je constate qu’une fois encore, un des chiots s’est aventuré hors du tas de couvertures qui leur sert de nid. Il se retrouve maintenant sur le carrelage lisse et froid, incapable de rejoindre sa mère et les autres. Mon enclos sera bien utile. Je réussis à  remettre la petite chose sur le tas grouillant de ses frères et soeurs, malgré les efforts de Silky, la mère un peu surprotectrice et nerveuse, pour me tenir à  distance (elle s’assied sur ses chiots et fait mine de vouloir prendre ma main dans sa gueule.) Le bruit cesse.

Sagar, endormi à  moins d’un mètre de moi, n’a pas bronché.

Nisha vient nettoyer le coin des chiots et nous y installons mon carton. Elle me félicite avec enthousiasme pour mon idée (qui rendra également le nettoyage plus aisé). Silky s’y installe aussitôt avec les chiots, que je n’ai presque pas entendu couiner depuis.

Histoire d’habituer un peu Silky à  ma présence, je m’installe sur un coin du lit avec mon livre, pendant que Nisha passe le balai et la panosse dans tout l’appartement.