The question of offsetting only overlapping parallel line segments has been a long standing issue. Many solutions suggest using processing tools which will actually change the geometry of your lines, but I suspect you want the geometry to stay the same, just the visualisation to be different.
Taras's answer here appeared to be the closest match, however perhaps due to changes in QGIS since 2020 I couldn't get it to work. The fundamental idea from Kazuhito's answer is the same though - (1) explode the lines into segments so you can compare identical geometries, and (2) get a count of how many lines overlap.
If you are willing to first explode your lines into segments as part of your workflow, this might work:
Sample data below - 3 lines with unique line_id value.
NOTE: In this example, Lines 1 (green) and 2 (blue) only partially overlap with Line 3 (pink), but Lines 1 and 2 do NOT have a vertex in the red circled area where Line 3 splits off. If your data is like this, please make sure you follow step 1A instead.
Explode lines
Step 1. Explode your line layer into segments using Processing Tools > Explode Lines.
Step 1A. (ALTERNATIVE TO 1) If your data is like the sample shown above, instead use Processing Tools > Split Lines with Lines. You will need SAGA (from QGIS 3.28 you may need to install it as a plugin).
For some reason I also ended up with a lot of zero-length lines and one extremely short line that I couldn't clean using processing tools, so I used Select Features by Expression > round($length,3) = 0
> Delete Selected Features on the result.
Either way your result should now look like this - one feature between each section that overlaps
Generate offset for overlapping lines
Step 2. In the layer symbology for your exploded lines, go to Offset and use the following expression in the data defined override:
array_find(array_agg($id,geom_to_wkt($geometry)),$id)
If you would like the line stacking to follow a certain order, e.g. based on line_id
(so the lowest line_id
always stays the most interior), you can use this expression - change line_id
to desired field:
array_find(array_agg($id,geom_to_wkt($geometry),order_by:="line_id"),$id)
You can use this in combination with categorised/graduated symbology by clicking on the main symbol as shown below, and then editing the offset in there.
Note either way the non-overlapping lines (eg in your example the lines going to the houses) are not offset.
Result: