Generate thumbnail image in addition to main image? (command line render)
Here's a second option, which I now believe is the best solution, as it is the most elegant and versatile that I've found so far.

The solution is essentially an extension of the Compositing nodes Scale and FileOutput option. However the critical difference is the addition of an Image node being mixed with the scaled Render Output image before being piped into the FileOutput node. The image node uses a dummy UV image created at the required Thumbnail output dimensions. When mixed with this dummy image, the FileOutput node will output the new dimensions without having to change the Scene Render Dimensions.

This option is better than my original solution because it is simpler to implement, shorter code, as well as not needing to rely on external file paths. 



Command line usage (last argument pipes in thumbnail width):

blender -b "file.blend" -o "//out/" -P thumbnail.py -- 500


and the script thumbnail.py:

import bpy
import sys

class Thumbnail:

    tWidthArg = int(sys.argv[9]) 

    scn = bpy.context.scene
    resX = scn.render.resolution_x
    resY = scn.render.resolution_y
    thumbWidth = 400

    if tWidthArg &gt; 0:
        thumbWidth = tWidthArg

    thumbHeight = ((thumbWidth/resX)*resY)

    #create new internal dummy image the size we want for thumbnail
    bpy.ops.image.new(name="ThumbNailDummyImage", width=thumbWidth, height=thumbHeight, color=(0.0, 0.0, 0.0, 0.0), alpha=False, generated_type='BLANK', float=False)

    #set up compositing nodes
    nTree = scn.node_tree
    Src = nTree.nodes["Render Layers"]
    Dst = nTree.nodes["Composite"]

    #image input node - use dummy uv image
    imageNode = nTree.nodes.new('CompositorNodeImage')
    imageNode.name = "INPUTIMAGE"
    imageNode.image = bpy.data.images['ThumbNailDummyImage']
    imageNode.location = (Src.location.x+300, Src.location.y-600)

    #scale node to resize thumbnail to correct size
    scaleNode = nTree.nodes.new('CompositorNodeScale')
    scaleNode.space = "ABSOLUTE"
    scaleNode.location = (imageNode.location.x, imageNode.location.y+200)
    scaleNode.inputs['X'].default_value = thumbWidth
    scaleNode.inputs['Y'].default_value = thumbHeight

    #mix the scale render output with the dummy uv image to force new output size
    mixNode = nTree.nodes.new('CompositorNodeMixRGB')
    mixNode.location = (imageNode.location.x+300, imageNode.location.y)

    #setup output filenode
    fileOutNode  = nTree.nodes.new('CompositorNodeOutputFile')
    fileOutNode.location = (mixNode.location.x+300, mixNode.location.y)
    fileOutNode.layer_slots[0].name='Thumb'
    fileOutNode.file_slots[0].path='Thumb'
    fileOutNode.format.file_format= 'JPEG'
    fileOutNode.format.color_mode = 'RGB'
    fileOutNode.base_path = '//out/'

    #connect nodes
    nTree.links.new(scaleNode.inputs[0], Src.outputs[0])
    nTree.links.new(mixNode.inputs[2], scaleNode.outputs[0])
    nTree.links.new(mixNode.inputs[1], imageNode.outputs[0])
    nTree.links.new(fileOutNode.inputs[0], mixNode.outputs[0])