Just a note, the root of the game is always a viewport, it's how the game renders. So that's why changing stuff with the root will scale up the image.
To make it so only one part of the game scales up you need a separate viewport. I haven't really messed with viewports before, so I don't know if this is completely the correct way, or the best way to do this. But I think it's working.
This is what the main scene looks like.
There is a ColorFrame, which stretches to the whole size of the screen (using "Full Rect" anchor). And the color is set to black, this is only so the extra pixels at the edge of the screen are black.
The ViewportSprite is what actually renders the Viewport. You'll have to set the viewport after you create it. The only thing I changed here was turning off the "Centered" option.
The Viewport is where all of the game nodes will go. This is what will be scaling up to render the game. The script for the viewport is this:
export(NodePath) var viewport_sprite_path
onready var viewport_sprite = get_node(viewport_sprite_path)
onready var base_size = self.get_rect().size
get_tree().connect("screen_resized", self, "_on_screen_resized")
var new_window_size = OS.get_window_size()
var scale_w = max(int(new_window_size.x / base_size.x), 1)
var scale_h = max(int(new_window_size.y / base_size.y), 1)
var scale = min(scale_w, scale_h)
#This offset is needed to keep pixels square
var offset = ((new_window_size / scale) - (new_window_size / scale).floor()) * scale
var offsethalf = (offset * 0.5).floor()
self.set_rect(Rect2(Vector2(), new_window_size / scale))
viewport_sprite.set_scale(Vector2(1, 1) * scale)
There is an export variable here, it's a NodePath to the ViewportSprite, so you'll have to set that as well. As for all of the options for the Viewport, this is what I've set (The "Rect" option can be set to whatever your base screen resolution is:
It's important to note that all of the children of the Viewport node can't be edited in the editor's viewport, like dragging nodes around or rotating them, it doesn't work. So it's probably best if you make all of the game nodes in a separate scene and then make an instance of that scene to set as the child of the Viewport, making it easier to edit.
The final node is a CanvasLayer, this will hold all of the GUI nodes and wont be scaled up like the Viewport. So everything here will stay native size. Of course this means that you can't have GUI elements that appear behind things in the game, they are separate layers.