The single best thing you can do to reduce disk I/O load is to ensure that your database fits comfortably in RAM after allocating RAM to everything else running on the system. If this is the case, your disk I/O will be primarily WAL flushes on commit, which is mostly sequential I/O.
The second thing you can do is address the shared_buffers
and effective_cache_size
settings. If the system is exclusively running PostgreSQL I like to set shared_buffers low, and effective_cache_size high and adjust from there. If the system is running other programs too, shared_buffers should be set much higher because it reserves RAM exclusively for PostgreSQL. Note that the OS disk cache is often faster than the PostgreSQL cache, but the Pg cache is more full-featured so there is a real balance. Measure, test, repeat.
A third setting to be aware of is work_mem.
This setting should be adjusted only with care, because it is per operation. If you set it too low, joins and sorts will write to disk. If you set it too high, it will squeeze out memory that could be used for caching data. Again, look for query performance patterns and move forward.